<script lang="ts">
  import { onDestroy, onMount, tick } from 'svelte';
  import { Terminal } from '@xterm/xterm';
  import '@xterm/xterm/css/xterm.css';
  import { api, post, del, wsUrl } from '../lib/api';
  import { requireConfirm, requireDangerConfirm, withConfirm } from '../lib/confirm';
  export let id: string;
  let status: any = {};
  let containers: any[] = [];
  let images: any[] = [];
  let volumes: any[] = [];
  let composeProjects: any[] = [];
  let logs = '';
  let selectedContainer = '';
  let containerDetail: any = null;
  let containerStats: any = null;
  let execContainer: any = null;
  let execEl: HTMLDivElement;
  let execTerm: Terminal | null = null;
  let execWs: WebSocket | null = null;
  let imageName = '';
  let pulling = false;
  let runImage = 'nginx:latest';
  let runName = '';
  let runPorts = '8080:80';
  let runVolumes = '';
  let runEnv = '';
  let runRestart = 'unless-stopped';
  let runCommand = '';
  let running = false;
  let composeProject = 'lightops-app';
  let composeWorkDir = '/opt/lightops-compose/lightops-app';
  let composeContent = `services:
  app:
    image: nginx:latest
    ports:
      - "8080:80"
    restart: unless-stopped
`;
  let deployingCompose = false;
  let proxyVisible = false;
  let proxyTarget = '';
  let proxySiteName = '';
  let proxyServerName = '';
  let proxyUpstream = 'http://127.0.0.1:3000';
  let proxyIssueSsl = false;
  let proxySslEmail = '';
  let creatingProxy = false;
  let error = '';
  onMount(load);
  onDestroy(closeExec);
  async function load() {
    error = '';
    try {
      status = await api(`/api/agents/${id}/docker/status`);
      containers = ((await api(`/api/agents/${id}/docker/containers`)) as any).containers || [];
      images = ((await api(`/api/agents/${id}/docker/images`)) as any).images || [];
      volumes = ((await api(`/api/agents/${id}/docker/volumes`)) as any).volumes || [];
      composeProjects = ((await api(`/api/agents/${id}/docker/compose/projects`)) as any).projects || [];
    } catch (err: any) {
      error = err.message;
    }
  }
  async function act(cid: string, action: string) {
    const label = ({ start: '启动', stop: '停止', restart: '重启' } as Record<string, string>)[action] || action;
    if (action === 'start') {
      if (!requireConfirm(`确认${label}容器 ${cid}？`)) return;
      await post(`/api/agents/${id}/docker/containers/${cid}/${action}`, {});
    } else {
      const confirmInfo = requireDangerConfirm(`${label}容器`, cid);
      if (!confirmInfo) return;
      await post(`/api/agents/${id}/docker/containers/${cid}/${action}`, withConfirm({}, confirmInfo));
    }
    await load();
  }
  async function rm(cid: string) {
    if (!requireDangerConfirm('删除容器', cid)) return;
    await del(`/api/agents/${id}/docker/containers/${cid}`);
    await load();
  }
  async function showLogs(cid: string) {
    error = '';
    try {
      const data: any = await api(`/api/agents/${id}/docker/containers/${cid}/logs`);
      logs = data.stdout || data.stderr || '';
    } catch (err: any) {
      error = err.message;
    }
  }

  async function showContainerDetail(cid: string) {
    error = '';
    selectedContainer = cid;
    containerDetail = null;
    containerStats = null;
    try {
      const [detail, stats]: any[] = await Promise.all([
        api(`/api/agents/${id}/docker/containers/${cid}/inspect`),
        api(`/api/agents/${id}/docker/containers/${cid}/stats`)
      ]);
      containerDetail = detail.detail || detail;
      containerStats = stats.stats || stats;
    } catch (err: any) {
      error = err.message;
    }
  }

  async function openExec(container: any) {
    closeExec();
    execContainer = container;
    await tick();
    execTerm = new Terminal({
      cursorBlink: true,
      fontFamily: 'JetBrains Mono, monospace',
      theme: { background: '#08110f' }
    });
    execTerm.open(execEl);
    execTerm.writeln(`正在进入容器 ${container.names || container.id} ...\r\n`);
    execWs = new WebSocket(wsUrl(`/api/agents/${id}/docker/containers/${encodeURIComponent(container.id)}/exec`));
    execWs.onopen = () => execTerm?.writeln('Docker exec 终端已连接，默认 shell: sh\r\n');
    execWs.onmessage = (ev) => execTerm?.write(ev.data);
    execWs.onclose = () => execTerm?.writeln('\r\n连接已关闭');
    execWs.onerror = () => execTerm?.writeln('\r\n连接失败或无权限');
    execTerm.onData((data) => execWs?.readyState === WebSocket.OPEN && execWs.send(data));
  }

  function closeExec() {
    execWs?.close();
    execWs = null;
    execTerm?.dispose();
    execTerm = null;
    execContainer = null;
  }
  async function rmi(imageId: string) {
    if (!requireDangerConfirm('删除镜像', imageId)) return;
    await del(`/api/agents/${id}/docker/images/${encodeURIComponent(imageId)}`);
    await load();
  }

  async function removeVolume(name: string) {
    if (!requireDangerConfirm('删除 Docker 数据卷', name)) return;
    await del(`/api/agents/${id}/docker/volumes/${encodeURIComponent(name)}`);
    await load();
  }

  async function pullImage() {
    const image = imageName.trim();
    if (!image) {
      error = '请输入镜像名称，例如 nginx:latest';
      return;
    }
    if (!requireConfirm(`确认拉取镜像 ${image}？大镜像可能需要较长时间。`)) return;
    error = '';
    logs = '';
    pulling = true;
    try {
      const data: any = await post(`/api/agents/${id}/docker/images/pull`, { image });
      logs = data.stdout || data.stderr || '镜像拉取完成';
      imageName = '';
      await load();
    } catch (err: any) {
      error = err.message;
    } finally {
      pulling = false;
    }
  }

  async function runContainer() {
    const image = runImage.trim();
    if (!image) {
      error = '请输入镜像名称';
      return;
    }
    const confirmInfo = requireDangerConfirm('创建并启动容器', runName.trim() || image);
    if (!confirmInfo) return;
    error = '';
    logs = '';
    running = true;
    try {
      const data: any = await post(`/api/agents/${id}/docker/containers/run`, withConfirm({
        image,
        name: runName.trim() || null,
        ports: parsePorts(runPorts),
        volumes: parseVolumes(runVolumes),
        env: parseEnv(runEnv),
        restart: runRestart,
        detach: true,
        command: runCommand.trim() || null
      }, confirmInfo));
      logs = data.stdout || data.stderr || '容器已创建';
      await load();
    } catch (err: any) {
      error = err.message;
    } finally {
      running = false;
    }
  }

  async function deployCompose() {
    const project = composeProject.trim();
    const workDir = composeWorkDir.trim();
    if (!project || !workDir || !composeContent.trim()) {
      error = '请填写项目名、工作目录和 Compose 内容';
      return;
    }
    const confirmInfo = requireDangerConfirm('部署 Compose 项目', project);
    if (!confirmInfo) return;
    error = '';
    logs = '';
    deployingCompose = true;
    try {
      const data: any = await post(`/api/agents/${id}/docker/compose/deploy`, withConfirm({
        project,
        work_dir: workDir,
        content: composeContent
      }, confirmInfo));
      logs = data.stdout || data.stderr || 'Compose 项目已部署';
      await load();
    } catch (err: any) {
      error = err.message;
    } finally {
      deployingCompose = false;
    }
  }

  async function composeAct(project: string, action: string) {
    const label = ({ start: '启动', stop: '停止', restart: '重启' } as Record<string, string>)[action] || action;
    const confirmInfo = action === 'start' ? null : requireDangerConfirm(`${label} Compose 项目`, project);
    if (action !== 'start' && !confirmInfo) return;
    if (action === 'start' && !requireConfirm(`确认${label} Compose 项目 ${project}？`)) return;
    await post(`/api/agents/${id}/docker/compose/projects/${encodeURIComponent(project)}/${action}`, withConfirm({}, confirmInfo));
    await load();
  }

  async function showComposeLogs(project: string) {
    error = '';
    try {
      const data: any = await api(`/api/agents/${id}/docker/compose/projects/${encodeURIComponent(project)}/logs`);
      logs = data.stdout || data.stderr || '';
    } catch (err: any) {
      error = err.message;
    }
  }

  function containerStatus(value: string) {
    if (!value) return '-';
    if (value.startsWith('Up')) return value.replace('Up', '运行中');
    if (value.startsWith('Exited')) return value.replace('Exited', '已退出');
    if (value.startsWith('Created')) return value.replace('Created', '已创建');
    if (value.startsWith('Restarting')) return value.replace('Restarting', '重启中');
    return value;
  }

  function parsePorts(value: string) {
    return value
      .split(/[,\n]+/)
      .map((item) => item.trim())
      .filter(Boolean)
      .map((item) => {
        const [host, containerProtocol] = item.split(':');
        const [container, protocol = 'tcp'] = (containerProtocol || '').split('/');
        return { host: Number(host), container: Number(container), protocol };
      })
      .filter((item) => item.host && item.container);
  }

  function parseVolumes(value: string) {
    return value
      .split(/[,\n]+/)
      .map((item) => item.trim())
      .filter(Boolean)
      .map((item) => {
        const parts = item.split(':');
        return { host: parts[0], container: parts[1], readonly: parts[2] === 'ro' };
      })
      .filter((item) => item.host && item.container);
  }

  function parseEnv(value: string) {
    const env: Record<string, string> = {};
    for (const line of value.split('\n')) {
      const trimmed = line.trim();
      if (!trimmed || !trimmed.includes('=')) continue;
      const index = trimmed.indexOf('=');
      env[trimmed.slice(0, index).trim()] = trimmed.slice(index + 1);
    }
    return env;
  }

  function openProxyForContainer(container: any) {
    proxyVisible = true;
    proxyTarget = container.names || container.id;
    proxySiteName = normalizeSiteName(container.names || container.id);
    proxyServerName = `${proxySiteName}.example.com`;
    proxyUpstream = `http://127.0.0.1:${extractHostPort(container.ports) || '3000'}`;
  }

  function openProxyForCompose(project: any) {
    proxyVisible = true;
    proxyTarget = project.name;
    proxySiteName = normalizeSiteName(project.name);
    proxyServerName = `${proxySiteName}.example.com`;
    proxyUpstream = 'http://127.0.0.1:3000';
  }

  async function createProxySite() {
    const site = proxySiteName.trim();
    const serverNameValue = proxyServerName.trim();
    const upstreamValue = proxyUpstream.trim();
    if (!site || !serverNameValue || !upstreamValue) {
      error = '请填写站点名、域名和上游地址';
      return;
    }
    const confirmInfo = requireDangerConfirm('创建并启用 Nginx 反代', site);
    if (!confirmInfo) return;
    error = '';
    logs = '';
    creatingProxy = true;
    try {
      const body = withConfirm(
        {
          name: site,
          server_name: serverNameValue,
          mode: 'proxy',
          upstream: upstreamValue,
          websocket: true,
          gzip: true,
          cache_static: true,
          client_max_body_size: '64m',
          source: 'docker',
          target: proxyTarget
        },
        confirmInfo
      );
      await post(`/api/agents/${id}/nginx/sites`, body);
      await post(`/api/agents/${id}/nginx/sites/${encodeURIComponent(site)}/enable`, withConfirm({}, confirmInfo));
      await post(`/api/agents/${id}/nginx/reload`, withConfirm({}, confirmInfo));
      if (proxyIssueSsl) {
        await post(`/api/agents/${id}/nginx/ssl/issue`, withConfirm({
          domains: serverNameValue.split(/[\s,]+/).filter(Boolean),
          email: proxySslEmail || null
        }, confirmInfo));
      }
      logs = `反代站点 ${site} 已创建，上游 ${upstreamValue}`;
      proxyVisible = false;
    } catch (err: any) {
      error = err.message;
    } finally {
      creatingProxy = false;
    }
  }

  function extractHostPort(ports: string) {
    const match = ports?.match(/(?:0\.0\.0\.0|127\.0\.0\.1|\[?::\]?|\*)?:(\d+)->/);
    return match?.[1] || '';
  }

  function normalizeSiteName(value: string) {
    return value
      .toLowerCase()
      .replace(/^[\/]+/, '')
      .replace(/[^a-z0-9._-]+/g, '-')
      .replace(/^-+|-+$/g, '')
      .slice(0, 64) || 'lightops-app';
  }

  function detailPairs(detail: any) {
    if (!detail) return [];
    const config = detail.Config || {};
    const host = detail.HostConfig || {};
    const state = detail.State || {};
    return [
      ['容器 ID', detail.Id || '-'],
      ['镜像', config.Image || detail.Image || '-'],
      ['状态', state.Status || '-'],
      ['启动时间', state.StartedAt || '-'],
      ['重启策略', host.RestartPolicy?.Name || '-'],
      ['网络模式', host.NetworkMode || '-'],
      ['工作目录', config.WorkingDir || '-'],
      ['入口命令', [config.Entrypoint, config.Cmd].flat().filter(Boolean).join(' ') || '-']
    ];
  }

  function mountList(detail: any) {
    return (detail?.Mounts || []).map((item: any) => `${item.Source || '-'} -> ${item.Destination || '-'}${item.RW === false ? ' (只读)' : ''}`);
  }

  function statValue(key: string) {
    return containerStats?.[key] || '-';
  }
</script>

<section class="panel">
  <h1>Docker</h1>
  <p class="muted">安装状态：{status.installed ? '已安装' : '未安装'} {status.version || ''}</p>
  {#if error}<p class="error">{error}</p>{/if}
  <h2>运行容器</h2>
  <div class="nginx-create">
    <div class="form-grid">
      <label>镜像<input bind:value={runImage} placeholder="nginx:latest" /></label>
      <label>容器名<input bind:value={runName} placeholder="可选，例如 my-nginx" /></label>
      <label>端口映射<input bind:value={runPorts} placeholder="8080:80,8443:443/tcp" /></label>
      <label>数据卷<input bind:value={runVolumes} placeholder="/host/data:/data,/host/conf:/conf:ro" /></label>
      <label>重启策略
        <select bind:value={runRestart}>
          <option value="no">不自动重启</option>
          <option value="always">always</option>
          <option value="unless-stopped">unless-stopped</option>
          <option value="on-failure">on-failure</option>
        </select>
      </label>
      <label>启动命令<input bind:value={runCommand} placeholder="可选，例如 server --port 3000" /></label>
    </div>
    <label>环境变量<textarea bind:value={runEnv} rows="4" placeholder="TZ=Asia/Shanghai&#10;PUID=1000"></textarea></label>
    <button on:click={runContainer} disabled={running}>{running ? '创建中...' : '创建并启动容器'}</button>
  </div>

  <h2>部署 Compose</h2>
  <div class="nginx-create">
    <div class="form-grid">
      <label>项目名<input bind:value={composeProject} placeholder="my-app" /></label>
      <label>工作目录<input bind:value={composeWorkDir} placeholder="/opt/lightops-compose/my-app" /></label>
    </div>
    <textarea bind:value={composeContent} rows="10" placeholder="docker compose yaml"></textarea>
    <button on:click={deployCompose} disabled={deployingCompose}>{deployingCompose ? '部署中...' : '保存并 up -d'}</button>
  </div>

  <h2>Docker Compose 项目</h2>
  <div class="table">
    {#each composeProjects as project}
      <div class="row">
        <strong>{project.name}</strong>
        <span>{project.status === 'Running' ? '运行中' : '已停止'}</span>
        <span>服务：{(project.services || []).join(', ') || '-'}</span>
        <span>容器：{(project.containers || []).length}</span>
        <button on:click={() => composeAct(project.name, 'start')}>启动</button>
        <button on:click={() => composeAct(project.name, 'restart')}>重启</button>
        <button on:click={() => composeAct(project.name, 'stop')}>停止</button>
        <button class="ghost" on:click={() => showComposeLogs(project.name)}>日志</button>
        <button class="ghost" on:click={() => openProxyForCompose(project)}>创建反代</button>
      </div>
    {/each}
    {#if composeProjects.length === 0}
      <div class="empty-state"><p class="muted">未发现 Docker Compose 项目。</p></div>
    {/if}
  </div>
  <h2>容器</h2>
  <div class="table">
    {#each containers as c}
      <div class="row docker-row">
        <strong>{c.names}</strong><span>{c.image}</span><span>{containerStatus(c.status)}</span><span>{c.ports || '-'}</span>
        <button on:click={() => act(c.id, 'start')}>启动</button>
        <button on:click={() => act(c.id, 'restart')}>重启</button>
        <button on:click={() => act(c.id, 'stop')}>停止</button>
        <button class="ghost" on:click={() => openExec(c)}>终端</button>
        <button class="ghost" on:click={() => showContainerDetail(c.id)}>详情/资源</button>
        <button class="ghost" on:click={() => showLogs(c.id)}>日志</button>
        <button class="ghost" on:click={() => openProxyForContainer(c)}>创建反代</button>
        <button class="danger" on:click={() => rm(c.id)}>删除</button>
      </div>
    {/each}
  </div>
  {#if execContainer}
    <h2>容器终端</h2>
    <div class="panel inner terminal-panel">
      <div class="title-row">
        <div>
          <strong>{execContainer.names || execContainer.id}</strong>
          <p class="muted">通过 docker exec -it 进入容器，只连接当前容器，不开放任意宿主机命令。</p>
        </div>
        <button class="ghost" on:click={closeExec}>关闭终端</button>
      </div>
      <div bind:this={execEl} class="terminal"></div>
    </div>
  {/if}
  {#if selectedContainer}
    <h2>容器详情</h2>
    <div class="panel inner">
      <div class="title-row">
        <div>
          <strong>{selectedContainer}</strong>
          <p class="muted">资源占用来自 docker stats --no-stream，配置来自 docker inspect。</p>
        </div>
        <button class="ghost" on:click={() => showContainerDetail(selectedContainer)}>刷新详情</button>
      </div>
      <div class="metric-grid">
        <div><b>{statValue('CPUPerc')}</b><span>CPU</span></div>
        <div><b>{statValue('MemUsage')}</b><span>内存</span></div>
        <div><b>{statValue('NetIO')}</b><span>网络</span></div>
        <div><b>{statValue('BlockIO')}</b><span>磁盘 IO</span></div>
      </div>
      <div class="detail-grid">
        {#each detailPairs(containerDetail) as pair}
          <div><span>{pair[0]}</span><strong>{pair[1]}</strong></div>
        {/each}
      </div>
      <h3>挂载目录</h3>
      {#if mountList(containerDetail).length === 0}
        <p class="muted">无挂载目录。</p>
      {:else}
        <div class="permission-list">
          {#each mountList(containerDetail) as mount}
            <span>{mount}</span>
          {/each}
        </div>
      {/if}
    </div>
  {/if}
  {#if proxyVisible}
    <h2>一键创建 Nginx 反代</h2>
    <div class="nginx-create">
      <p class="muted">目标：{proxyTarget}。会创建站点、启用站点，并执行 nginx -t 后重载。</p>
      <div class="form-grid">
        <label>站点名<input bind:value={proxySiteName} placeholder="my-app" /></label>
        <label>域名<input bind:value={proxyServerName} placeholder="app.example.com" /></label>
        <label>上游地址<input bind:value={proxyUpstream} placeholder="http://127.0.0.1:3000" /></label>
        <label>证书邮箱<input bind:value={proxySslEmail} placeholder="可选，用于 certbot" /></label>
        <label class="check"><input type="checkbox" bind:checked={proxyIssueSsl} /> 同时申请免费 SSL 证书</label>
      </div>
      <div class="actions">
        <button on:click={createProxySite} disabled={creatingProxy}>{creatingProxy ? '创建中...' : '创建并启用反代'}</button>
        <button class="ghost" on:click={() => (proxyVisible = false)}>取消</button>
      </div>
    </div>
  {/if}
  {#if logs}
    <h2>容器日志</h2>
    <pre class="log-output">{logs}</pre>
  {/if}
  <h2>镜像</h2>
  <div class="inline-form">
    <input bind:value={imageName} placeholder="镜像名称，例如 nginx:latest、redis:7" />
    <button on:click={pullImage} disabled={pulling}>{pulling ? '拉取中...' : '拉取镜像'}</button>
  </div>
  <div class="table">
    {#each images as img}
      <div class="row">
        <strong>{img.repository}:{img.tag}</strong><span>{img.id}</span><span>{img.size}</span>
        <button class="danger" on:click={() => rmi(img.id)}>删除镜像</button>
      </div>
    {/each}
  </div>

  <h2>数据卷</h2>
  <p class="muted">删除数据卷会删除持久化数据。只建议删除确认不再被容器使用的卷。</p>
  <div class="table">
    {#each volumes as volume}
      <div class="row volume-row">
        <strong>{volume.Name || volume.name}</strong>
        <span>驱动：{volume.Driver || '-'}</span>
        <span>挂载点：{volume.Mountpoint || '-'}</span>
        <button class="danger" on:click={() => removeVolume(volume.Name || volume.name)}>删除数据卷</button>
      </div>
    {/each}
    {#if volumes.length === 0}
      <div class="empty-state"><p class="muted">未发现 Docker 数据卷。</p></div>
    {/if}
  </div>
</section>
