<script lang="ts">
  import { api } from "./api.js";
  import { toastOk, toastErr } from "./stores.js";
  import { selectedFilePath, filesVersion } from "./browser.js";
  import type { FileEntry, NodeDetail } from "./types.js";
  import Modal from "./Modal.svelte";

  let { node }: { node: NodeDetail } = $props();

  let files = $state<FileEntry[] | null>(null);
  let loadError = $state<string | null>(null);
  let showNewFile = $state(false);
  let newPath = $state("");
  let newContent = $state("");
  let uploadInput: HTMLInputElement;

  const canEdit = $derived(node.role !== "VIEW");

  async function loadFiles(): Promise<void> {
    try {
      const r = await api<{ files: FileEntry[] }>(`/database/api/projects/${node.id}/files`);
      files = r.files;
      loadError = null;
    } catch (e) {
      loadError = e instanceof Error ? e.message : String(e);
    }
  }

  $effect(() => {
    void node.id;
    void $filesVersion;
    void loadFiles();
  });

  async function submitNewFile(): Promise<void> {
    const path = newPath.trim();
    if (path === "") return;
    try {
      await api(`/database/api/projects/${node.id}/file`, {
        method: "PUT",
        body: { path, content: newContent },
      });
      toastOk("已创建");
      showNewFile = false;
      newPath = ""; newContent = "";
      await loadFiles();
    } catch (e) {
      toastErr(e instanceof Error ? e.message : String(e));
    }
  }

  function u8ToBase64(bytes: Uint8Array): string {
    let bin = "";
    const CHUNK = 0x8000;
    for (let i = 0; i < bytes.length; i += CHUNK) {
      bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)) as unknown as number[]);
    }
    return btoa(bin);
  }

  async function doUpload(e: Event): Promise<void> {
    const input = e.target as HTMLInputElement;
    const file = input.files?.[0];
    input.value = "";
    if (!file) return;
    if (file.size > 10 * 1024 * 1024) {
      toastErr("文件超过 10MB 上限");
      return;
    }
    const targetPath = prompt("保存到路径(可含目录):", "材料/" + file.name);
    if (!targetPath) return;
    const bytes = new Uint8Array(await file.arrayBuffer());
    const isBinary = bytes.includes(0);
    const body = isBinary
      ? { path: targetPath, content: u8ToBase64(bytes), encoding: "base64" }
      : { path: targetPath, content: new TextDecoder("utf-8").decode(bytes), encoding: "utf8" };
    try {
      await api(`/database/api/projects/${node.id}/file`, { method: "PUT", body });
      toastOk("已上传 " + file.name);
      await loadFiles();
    } catch (err) {
      toastErr(err instanceof Error ? err.message : String(err));
    }
  }
</script>

<div class="rounded-xl border border-line-soft bg-panel p-5">
  <div class="mb-2 flex items-center justify-between">
    <div class="text-[13px] font-semibold">项目文件({files?.length ?? 0})</div>
    {#if canEdit}
      <div class="flex gap-2">
        <button class="rounded-lg border border-line bg-panel px-3 py-1.5 text-[12.5px] font-medium text-ink transition hover:bg-hover" onclick={() => (showNewFile = true)}>+ 新建文件</button>
        <button class="rounded-lg bg-accent px-3 py-1.5 text-[12.5px] font-medium text-white transition hover:bg-accent-hover" onclick={() => uploadInput.click()}>上传文件</button>
        <input bind:this={uploadInput} type="file" class="hidden" onchange={doUpload} />
      </div>
    {/if}
  </div>

  {#if files === null && loadError === null}
    <div class="py-5 text-center text-xs text-ink-3">加载中…</div>
  {:else if loadError}
    <div class="py-5 text-center text-xs text-danger">{loadError}</div>
  {:else if files && files.length === 0}
    <div class="py-5 text-center text-xs text-ink-3">空仓库 · 可新建或上传文件</div>
  {:else if files}
    <table class="w-full border-collapse text-[13px]">
      <tbody>
        {#each files as f (f.path)}
          <tr
            class="cursor-pointer border-t border-line-soft first:border-t-0 {$selectedFilePath === f.path ? 'bg-selected' : 'hover:bg-hover'}"
            onclick={() => selectedFilePath.set(f.path)}
          >
            <td class="py-2 font-mono text-[12.5px] text-ink">{f.path}</td>
            <td class="py-2 text-right font-mono text-[11px] text-ink-3">{f.size} B</td>
          </tr>
        {/each}
      </tbody>
    </table>
  {/if}
</div>

{#if showNewFile}
  <Modal title="新建文件" onclose={() => (showNewFile = false)}>
    <div class="mb-3">
      <label class="mb-1 block text-[11.5px] text-ink-3" for="nf-path">路径</label>
      <input id="nf-path" class="w-full rounded-lg border border-line bg-panel px-3 py-2 font-mono text-[13px] outline-none focus:border-accent" bind:value={newPath} placeholder="docs/intro.md" />
    </div>
    <div class="mb-3">
      <label class="mb-1 block text-[11.5px] text-ink-3" for="nf-content">内容</label>
      <textarea id="nf-content" rows="8" class="w-full rounded-lg border border-line bg-panel px-3 py-2 font-mono text-xs outline-none focus:border-accent" bind:value={newContent} placeholder="内容…"></textarea>
    </div>
    <div class="mt-4 flex justify-end gap-2">
      <button class="rounded-lg border border-line bg-panel px-3.5 py-1.5 text-[12.5px] font-medium text-ink transition hover:bg-hover" onclick={() => (showNewFile = false)}>取消</button>
      <button class="rounded-lg bg-accent px-3.5 py-1.5 text-[12.5px] font-medium text-white transition hover:bg-accent-hover" onclick={submitNewFile}>创建</button>
    </div>
  </Modal>
{/if}
