import type { ReactElement } from "react";
import { FormEvent, useEffect, useState } from "react";

import { api } from "../../services/api/client";
import type { SkillFileResponse, SkillItem, SkillsResponse, SkillTreeResponse } from "../../types/domain";
import { jsonPreview, toneClass } from "../../utils";
import { StateTag } from "../shared/StateTag";

interface SkillsState {
  loading: boolean;
  uploadPending: boolean;
  items: SkillItem[];
  assembly: SkillsResponse["agentRunAssembly"];
  selectedId: string | null;
  tree: SkillTreeResponse | null;
  preview: SkillFileResponse["file"] | null;
  error: string | null;
}

const initialState: SkillsState = { loading: false, uploadPending: false, items: [], assembly: null, selectedId: null, tree: null, preview: null, error: null };

export function SkillsView(): ReactElement {
  const [state, setState] = useState<SkillsState>(initialState);

  async function load(force = false): Promise<void> {
    if (state.loading && !force) return;
    setState((current) => ({ ...current, loading: true, error: null }));
    const response = await api.skills();
    if (!response.ok) {
      setState((current) => ({ ...current, loading: false, items: [], assembly: null, error: response.error ?? "skills 列表不可用" }));
      return;
    }
    const items = response.data?.skills ?? [];
    const selectedId = state.selectedId && items.some((item) => item.id === state.selectedId) ? state.selectedId : items[0]?.id ?? null;
    setState((current) => ({ ...current, loading: false, items, assembly: response.data?.agentRunAssembly ?? null, selectedId }));
    if (selectedId) await loadDetail(selectedId);
  }

  async function loadDetail(skillId: string): Promise<void> {
    setState((current) => ({ ...current, selectedId: skillId, error: null }));
    const [tree, preview] = await Promise.all([api.skillTree(skillId), api.skillFile(skillId, "SKILL.md")]);
    setState((current) => ({
      ...current,
      tree: tree.ok ? tree.data : null,
      preview: preview.ok ? preview.data?.file ?? null : { path: "SKILL.md", content: preview.error ?? "文件不可预览", status: "failed" },
      error: tree.ok ? current.error : tree.error
    }));
  }

  async function upload(event: FormEvent<HTMLFormElement>): Promise<void> {
    event.preventDefault();
    const input = event.currentTarget.querySelector<HTMLInputElement>("#skill-upload-input");
    const files = Array.from(input?.files ?? []);
    if (files.length === 0 || state.uploadPending) return;
    setState((current) => ({ ...current, uploadPending: true, error: null }));
    try {
      const payload = await Promise.all(files.map(readUploadFile));
      const response = await api.uploadSkills(payload);
      if (!response.ok) {
        setState((current) => ({ ...current, error: response.error ?? "skill 上传失败" }));
      } else {
        if (input) input.value = "";
        await load(true);
      }
    } finally {
      setState((current) => ({ ...current, uploadPending: false }));
    }
  }

  useEffect(() => { void load(); }, []);

  const count = state.items.length;
  const statusText = state.uploadPending ? "上传中" : state.loading ? "加载中" : state.error ? "阻塞" : `${count} 个 skill`;
  return (
    <section className="view skills-view" id="skills" data-view="skills" aria-labelledby="skills-title">
      <div className="workspace-panel skills-panel">
        <div className="panel-title-row">
          <div><p className="eyebrow">Skills</p><h2 id="skills-title">Skill 管理</h2></div>
          <div className="skills-actions"><StateTag id="skill-status" tone={toneClass(state.error ? "blocked" : state.loading ? "pending" : "source")}>{statusText}</StateTag><button className="command-button secondary" id="skill-refresh" type="button" onClick={() => void load(true)}>刷新</button></div>
        </div>
        <div className="skills-layout">
          <section className="skill-upload-panel" aria-label="上传 skill">
            <form id="skill-upload-form" className="skill-upload-form" onSubmit={upload}>
              <label className="skill-upload-drop"><span>选择目录</span><input id="skill-upload-input" type="file" multiple webkitdirectory="" /></label>
              <button className="command-button" id="skill-upload-button" type="submit" disabled={state.uploadPending}>上传</button>
            </form>
            <AssemblyPanel assembly={state.assembly} />
            <div id="skill-list" className="skill-list" aria-label="Skill 列表" aria-live="polite">{state.items.map((item) => <button key={item.id} type="button" className={`skill-item${item.id === state.selectedId ? " active" : ""}`} data-skill-id={item.id} onClick={() => void loadDetail(item.id)}><strong>{item.name ?? item.title ?? item.id}</strong><span>{item.description ?? item.status ?? "source"}</span></button>)}</div>
          </section>
          <section className="skill-detail-panel" aria-label="Skill 文件"><div className="panel-title-row skill-detail-head"><div><p className="eyebrow">Skill Files</p><h3 id="skill-detail-title">{state.selectedId ?? "未选择 skill"}</h3></div></div><div id="skill-tree" className="skill-tree" aria-label="Skill 文件树">{(state.tree?.files ?? state.tree?.tree ?? []).map((file) => <button type="button" key={file.path}>{file.path}</button>)}</div></section>
          <section className="skill-preview-panel" aria-label="SKILL.md 预览"><pre id="skill-preview" className="skill-preview" aria-live="polite">{state.preview?.content ?? "等待 SKILL.md。"}</pre></section>
        </div>
      </div>
    </section>
  );
}

function AssemblyPanel({ assembly }: { assembly: SkillsResponse["agentRunAssembly"] }): ReactElement {
  if (!assembly) return <div id="skill-assembly" className="skill-assembly" aria-live="polite">AgentRun skill 装配摘要未返回。</div>;
  const promptRefs = assembly.promptAssembly?.refs ?? assembly.promptRefs ?? [];
  return (
    <div id="skill-assembly" className="skill-assembly" aria-live="polite">
      <div className={`skill-assembly-body tone-border-${toneClass(assembly.status)}`}>
        <div className="skill-assembly-head"><strong className="skill-name">AgentRun 装配</strong><StateTag tone={toneClass(assembly.status)}>{assembly.status ?? "unknown"}</StateTag></div>
        <div className="skill-meta"><span>skills={assembly.counts?.skillRefs ?? assembly.skillRefs?.length ?? 0}</span><span>prompts={assembly.counts?.promptRefs ?? promptRefs.length}</span><span>tools={assembly.counts?.toolAliases ?? assembly.toolAliases?.length ?? 0}</span></div>
        <div className="skill-prompt-assembly"><div className="skill-prompt-head"><span className="skill-prompt-title">引导 prompt 装配</span><StateTag tone={toneClass(assembly.promptAssembly?.status ?? assembly.status)}>{assembly.promptAssembly?.status ?? "source"}</StateTag></div><div className="skill-prompt-list">{promptRefs.map((item) => <div className="skill-prompt-row" key={item.name}><span className="skill-prompt-name mono wrap">{item.name}</span><span>{item.required ? "required" : "optional"}</span></div>)}</div></div>
        <pre className="skill-meta mono wrap">ResourceBundleRef.promptRefs {jsonPreview(promptRefs, 600)}</pre>
      </div>
    </div>
  );
}

async function readUploadFile(file: File): Promise<{ relativePath: string; sizeBytes: number; contentBase64: string }> {
  const bytes = new Uint8Array(await file.arrayBuffer());
  let binary = "";
  const chunkSize = 0x8000;
  for (let offset = 0; offset < bytes.length; offset += chunkSize) binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
  return { relativePath: file.webkitRelativePath || file.name, sizeBytes: file.size, contentBase64: btoa(binary) };
}
