// Gitea 集成，拆两半（ui-restructure N4）：
// - GiteaInstancesSection：实例管理（全局配置）→ 挂设置页
// - GiteaPanel：当前项目仓库绑定 + webhook（项目级）→ 留项目蓝图治理面板
// 控制面在 gitea.rs，观测面（webhook 接收）在 mcp_server.rs
import { useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import {
  giteaSaveInstance,
  giteaListInstances,
  giteaDeleteInstance,
  giteaUpdateInstance,
  giteaTestConnection,
  giteaGetRepoLink,
  giteaListLinkedRepos,
  giteaCreateRepo,
  giteaMigrateRepo,
  giteaLinkRepo,
  giteaSetupWebhook,
  giteaUnlinkRepo,
  giteaPollNow,
  scanLocalGit,
  gitInitAndCommit,
  gitRemoteSet,
  gitPush,
  type GiteaInstance,
} from "../../lib/commands";
import { useUIStore } from "../../store/ui";

type BindMode = "create" | "migrate" | "link";

const CALLBACK_BASE_KEY = "gitea_callback_base";

const inputCls =
  "w-full text-[11px] border border-gray-200 rounded px-2 py-1.5 outline-none focus:border-blue-400";
const btnCls =
  "px-2 py-1 rounded text-[11px] border border-gray-200 text-gray-500 hover:bg-gray-50 transition-colors";

// ── 实例管理（全局，挂设置页）────────────────────────────────────────────────

export function GiteaInstancesSection() {
  const qc = useQueryClient();
  const showToast = useUIStore((s) => s.showToast);

  const { data: instances = [] } = useQuery({
    queryKey: ["gitea-instances"],
    queryFn: giteaListInstances,
  });

  const [showAdd, setShowAdd] = useState(false);
  const [instName, setInstName] = useState("");
  const [instUrl, setInstUrl] = useState("");
  const [instToken, setInstToken] = useState("");
  const [testing, setTesting] = useState<string | null>(null);
  const [editingTokenId, setEditingTokenId] = useState<string | null>(null);
  const [newToken, setNewToken] = useState("");

  const handleUpdateToken = async (id: string) => {
    if (!newToken.trim()) return;
    try {
      await giteaUpdateInstance(id, { access_token: newToken.trim() });
      qc.invalidateQueries({ queryKey: ["gitea-instances"] });
      setEditingTokenId(null);
      setNewToken("");
      showToast("Token 已更新 ✓ 可点「测试连接」验证");
    } catch (e) {
      showToast(`❌ ${e}`);
    }
  };

  const handleAdd = async () => {
    if (!instName.trim() || !instUrl.trim() || !instToken.trim()) return;
    try {
      await giteaSaveInstance({
        name: instName.trim(),
        url: instUrl.trim(),
        access_token: instToken.trim(),
        is_default: instances.length === 0,
      });
      qc.invalidateQueries({ queryKey: ["gitea-instances"] });
      setInstName(""); setInstUrl(""); setInstToken("");
      setShowAdd(false);
      showToast("Gitea 实例已添加 ✓");
    } catch (e) {
      showToast(`❌ ${e}`);
    }
  };

  const handleTest = async (inst: GiteaInstance) => {
    setTesting(inst.id);
    try {
      const user = await giteaTestConnection(inst.id);
      showToast(`✓ 连接成功，账户: ${user.login}`);
    } catch (e) {
      showToast(`❌ ${e}`);
    } finally {
      setTesting(null);
    }
  };

  const handleDelete = async (inst: GiteaInstance) => {
    if (!confirm(`删除实例「${inst.name}」？其下所有项目绑定会一并删除。`)) return;
    try {
      await giteaDeleteInstance(inst.id);
      qc.invalidateQueries({ queryKey: ["gitea-instances"] });
      qc.invalidateQueries({ queryKey: ["gitea-linked-repos"] });
      showToast("已删除");
    } catch (e) {
      showToast(`❌ ${e}`);
    }
  };

  return (
    <section className="bg-white rounded-xl border border-gray-200 shadow-sm px-6 py-5">
      <div className="flex items-center justify-between mb-4">
        <h2 className="text-sm font-semibold text-gray-700">🪁 Gitea 实例</h2>
        {!showAdd && (
          <button onClick={() => setShowAdd(true)} className="text-xs text-blue-500 hover:text-blue-600">
            + 添加实例
          </button>
        )}
      </div>

      <div className="space-y-2">
        {instances.length === 0 && !showAdd && (
          <p className="text-xs text-gray-400">尚未添加 Gitea 实例——添加后可在各项目蓝图的治理面板中绑定仓库</p>
        )}
        {instances.map((inst) => (
          <div key={inst.id} className="space-y-1.5">
            <div className="flex items-center gap-2 text-xs">
              <span className="font-medium text-gray-700 shrink-0">{inst.name}</span>
              {inst.is_default && (
                <span className="text-[10px] px-1 rounded bg-blue-50 text-blue-500 shrink-0">默认</span>
              )}
              <span className="text-gray-400 font-mono truncate flex-1">{inst.url}</span>
              <button onClick={() => handleTest(inst)} disabled={testing === inst.id} className={btnCls}>
                {testing === inst.id ? "…" : "测试连接"}
              </button>
              <button
                onClick={() => { setEditingTokenId(editingTokenId === inst.id ? null : inst.id); setNewToken(""); }}
                className={btnCls}
              >
                改 Token
              </button>
              <button
                onClick={() => handleDelete(inst)}
                className="px-2 py-1 rounded text-[11px] border border-gray-200 text-red-400 hover:bg-red-50"
              >
                删除
              </button>
            </div>
            {editingTokenId === inst.id && (
              <div className="flex gap-1.5 pl-2">
                <input
                  className={inputCls}
                  type="password"
                  placeholder="新 Access Token（需 repository 读写 + user 读写权限）"
                  value={newToken}
                  onChange={(e) => setNewToken(e.target.value)}
                  autoFocus
                />
                <button
                  onClick={() => handleUpdateToken(inst.id)}
                  disabled={!newToken.trim()}
                  className="px-3 py-1.5 rounded text-xs bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 shrink-0"
                >
                  更新
                </button>
              </div>
            )}
          </div>
        ))}
        {showAdd && (
          <div className="space-y-2 border-t border-gray-100 pt-3">
            <input className={inputCls} placeholder="实例名称（如：家里 NAS）" value={instName} onChange={(e) => setInstName(e.target.value)} />
            <input className={inputCls} placeholder="https://gitea.example.com" value={instUrl} onChange={(e) => setInstUrl(e.target.value)} />
            <input className={inputCls} type="password" placeholder="Access Token（用户设置 → 应用 → 生成令牌；权限：repository 读写 + user 读写）" value={instToken} onChange={(e) => setInstToken(e.target.value)} />
            <div className="flex gap-1.5">
              <button onClick={handleAdd} className="px-3 py-1.5 rounded text-xs bg-blue-600 text-white hover:bg-blue-700">保存</button>
              <button onClick={() => setShowAdd(false)} className="px-3 py-1.5 rounded text-xs border border-gray-200 text-gray-500 hover:bg-gray-50">取消</button>
            </div>
          </div>
        )}
      </div>
    </section>
  );
}

// ── 项目级绑定 + webhook（留项目蓝图治理面板）────────────────────────────────

interface Props {
  projectId: string;
  projectName: string;
  projectPath?: string;
}

export function GiteaPanel({ projectId, projectName, projectPath }: Props) {
  const qc = useQueryClient();
  const showToast = useUIStore((s) => s.showToast);

  const { data: instances = [] } = useQuery({
    queryKey: ["gitea-instances"],
    queryFn: giteaListInstances,
  });

  const { data: link } = useQuery({
    queryKey: ["gitea-link", projectId],
    queryFn: () => giteaGetRepoLink(projectId),
  });

  const { data: allLinks = [] } = useQuery({
    queryKey: ["gitea-linked-repos"],
    queryFn: giteaListLinkedRepos,
  });

  // G7 一键接线：绑定后检测本地 git 状态（无 git / 无 origin / 已接线）
  const { data: localGit, refetch: refetchLocalGit } = useQuery({
    queryKey: ["gitea-local-git", projectId],
    queryFn: () => scanLocalGit(projectPath!),
    enabled: !!link && !!projectPath,
    staleTime: 30000,
    retry: false,
  });
  const hasOrigin = localGit?.remotes.some((r) => r.name === "origin") ?? false;
  const needsWiring = !!link && !!projectPath && localGit != null && (!localGit.has_git || !hasOrigin);
  const [wiring, setWiring] = useState(false);

  const handleWireUp = async () => {
    if (!projectPath || !link?.clone_url) return;
    setWiring(true);
    try {
      if (!localGit?.has_git || !localGit?.has_commits) {
        await gitInitAndCommit(projectPath, "chore: 项目初始化（炼境接线）");
      }
      await gitRemoteSet(projectPath, link.clone_url);
      const msg = await gitPush(projectPath);
      showToast(`✓ 接线完成：${msg}`);
      refetchLocalGit();
    } catch (e) {
      showToast(`❌ 接线失败: ${e}`);
    } finally {
      setWiring(false);
    }
  };

  const refresh = () => {
    qc.invalidateQueries({ queryKey: ["gitea-link", projectId] });
    qc.invalidateQueries({ queryKey: ["gitea-linked-repos"] });
  };

  // ── 绑定操作 ──
  const [bindMode, setBindMode] = useState<BindMode | null>(null);
  const [bindInstanceId, setBindInstanceId] = useState("");
  const [repoName, setRepoName] = useState("");
  const [repoOwner, setRepoOwner] = useState("");
  const [cloneAddr, setCloneAddr] = useState("");
  const [migrateToken, setMigrateToken] = useState("");
  const [binding, setBinding] = useState(false);

  const effectiveInstanceId =
    bindInstanceId || instances.find((i) => i.is_default)?.id || instances[0]?.id || "";

  const handleBind = async () => {
    if (!effectiveInstanceId || !repoName.trim()) return;
    setBinding(true);
    try {
      if (bindMode === "create") {
        await giteaCreateRepo(effectiveInstanceId, projectId, repoName.trim(), projectName, true);
      } else if (bindMode === "migrate") {
        if (!cloneAddr.trim()) { showToast("❌ 请填写源仓库地址"); return; }
        await giteaMigrateRepo(
          effectiveInstanceId, projectId, cloneAddr.trim(), repoName.trim(),
          migrateToken.trim() || undefined, true
        );
      } else if (bindMode === "link") {
        if (!repoOwner.trim()) { showToast("❌ 请填写仓库 owner"); return; }
        await giteaLinkRepo(effectiveInstanceId, projectId, repoOwner.trim(), repoName.trim());
      }
      refresh();
      setBindMode(null);
      setRepoName(""); setRepoOwner(""); setCloneAddr(""); setMigrateToken("");
      showToast("仓库绑定成功 ✓");
    } catch (e) {
      showToast(`❌ ${e}`);
    } finally {
      setBinding(false);
    }
  };

  // ── Webhook ──
  const [callbackBase, setCallbackBase] = useState(
    () => localStorage.getItem(CALLBACK_BASE_KEY) ?? "http://127.0.0.1:27190"
  );
  const [settingWebhook, setSettingWebhook] = useState(false);
  const callbackUrl = `${callbackBase.replace(/\/+$/, "")}/webhook/gitea/${projectId}`;

  const handleSetupWebhook = async () => {
    setSettingWebhook(true);
    try {
      localStorage.setItem(CALLBACK_BASE_KEY, callbackBase);
      await giteaSetupWebhook(projectId, callbackBase);
      refresh();
      showToast("Webhook 已注册 ✓ push 事件将实时进入飞轮");
    } catch (e) {
      showToast(`❌ ${e}`);
    } finally {
      setSettingWebhook(false);
    }
  };

  const handleUnlink = async () => {
    if (!confirm("解绑当前项目与 Gitea 仓库？（Gitea 侧仓库不会删除）")) return;
    try {
      await giteaUnlinkRepo(projectId);
      refresh();
      showToast("已解绑");
    } catch (e) {
      showToast(`❌ ${e}`);
    }
  };

  const [polling, setPolling] = useState(false);
  const handlePollNow = async () => {
    setPolling(true);
    try {
      const r = await giteaPollNow();
      showToast(`✓ 轮询 ${r.polled} 个仓库，新采集 ${r.ingested} 条 commit`);
    } catch (e) {
      showToast(`❌ ${e}`);
    } finally {
      setPolling(false);
    }
  };

  const copy = (text: string) => {
    navigator.clipboard.writeText(text).catch(() => {});
    showToast("已复制 ✓");
  };

  return (
    <div className="bg-white rounded-lg border border-gray-100 p-3 space-y-3">
      <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider">
        🪁 Gitea 绑定
      </p>

      {/* ── 当前项目绑定 ── */}
      <div className="space-y-2">
        {link ? (
          <>
            <div className="flex items-center gap-2 text-[11px]">
              <span className="text-gray-500 shrink-0">已绑定</span>
              <span className="font-mono text-gray-700 truncate">
                {link.gitea_owner}/{link.gitea_repo_name}
              </span>
              {link.clone_url && (
                <button onClick={() => copy(link.clone_url!)} className="text-blue-400 hover:text-blue-600 shrink-0 text-[10px]">
                  复制 clone URL
                </button>
              )}
              <button onClick={handleUnlink} className="ml-auto text-red-400 hover:text-red-500 shrink-0 text-[10px]">
                解绑
              </button>
            </div>

            {/* G7 本地接线：仓库已建但本地未连通时的一次性引导 */}
            {needsWiring && (
              <div className="bg-indigo-50 border border-indigo-100 rounded p-2 space-y-1.5">
                <p className="text-[11px] text-indigo-700 font-medium">
                  {!localGit?.has_git ? "本地目录还不是 git 仓库" : "本地仓库尚未关联远端 origin"}
                </p>
                <div className="flex items-center gap-1.5">
                  <p className="flex-1 text-[10px] text-indigo-400 leading-relaxed">
                    {!localGit?.has_git
                      ? "将执行：git init + 首次提交 + 关联 origin + 推送"
                      : "将执行：关联 origin + 推送当前分支"}
                  </p>
                  <button
                    onClick={handleWireUp}
                    disabled={wiring}
                    className="px-2.5 py-1 rounded text-[11px] bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-50 shrink-0"
                  >
                    {wiring ? "接线中…" : "🔌 一键接线"}
                  </button>
                </div>
                <p className="text-[10px] text-indigo-300 leading-relaxed">
                  接线是一次性动作；日常 commit / push 在项目自己的 AI 会话中进行。
                </p>
              </div>
            )}

            {/* webhook 状态与注册 */}
            <div className="bg-gray-50 rounded p-2 space-y-1.5">
              <div className="flex items-center gap-1.5 text-[11px]">
                <span className={`w-1.5 h-1.5 rounded-full ${link.webhook_id ? "bg-green-400" : "bg-gray-300"}`} />
                <span className="text-gray-500">
                  {link.webhook_id ? `Webhook 已注册（#${link.webhook_id}）` : "Webhook 未注册"}
                </span>
              </div>
              <div className="flex gap-1.5">
                <input
                  className={inputCls}
                  value={callbackBase}
                  onChange={(e) => setCallbackBase(e.target.value)}
                  placeholder="Gitea 服务器可达的宿主机地址，如 http://192.168.1.5:27190"
                />
              </div>
              <div className="flex items-center gap-1.5">
                <code className="flex-1 text-[10px] text-gray-400 font-mono truncate" title={callbackUrl}>
                  {callbackUrl}
                </code>
                <button onClick={() => copy(callbackUrl)} className={btnCls}>复制</button>
                <button
                  onClick={handleSetupWebhook}
                  disabled={settingWebhook}
                  className="px-2.5 py-1 rounded text-[11px] bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
                >
                  {settingWebhook ? "注册中…" : link.webhook_id ? "重设" : "注册 Webhook"}
                </button>
              </div>
              <p className="text-[10px] text-gray-400 leading-relaxed">
                需 Gitea 服务器能访问上述地址（MCP 端口默认 27190）。push 事件将实时写入飞轮 commit 数据。
              </p>
              <div className="flex items-center gap-1.5 border-t border-gray-100 pt-1.5">
                <p className="flex-1 text-[10px] text-gray-400 leading-relaxed">
                  公网 Gitea + 内网炼境时 webhook 不可达——已由后台轮询兜底（每 10 分钟出站拉取）。
                </p>
                <button
                  onClick={handlePollNow}
                  disabled={polling}
                  className={btnCls}
                >
                  {polling ? "拉取中…" : "立即拉取"}
                </button>
              </div>
            </div>
          </>
        ) : instances.length === 0 ? (
          <p className="text-[11px] text-gray-400">
            尚未配置 Gitea 实例——请先在「设置 → Gitea 实例」中添加
          </p>
        ) : (
          <>
            <div className="flex items-center gap-1.5">
              <span className="text-[11px] text-gray-500">本项目未绑定：</span>
              {(["create", "migrate", "link"] as BindMode[]).map((m) => (
                <button
                  key={m}
                  onClick={() => setBindMode(bindMode === m ? null : m)}
                  className={`px-2 py-1 rounded text-[11px] border transition-colors ${
                    bindMode === m
                      ? "border-blue-300 bg-blue-50 text-blue-600"
                      : "border-gray-200 text-gray-500 hover:bg-gray-50"
                  }`}
                >
                  {m === "create" ? "新建仓库" : m === "migrate" ? "迁移" : "绑定已有"}
                </button>
              ))}
            </div>
            {bindMode && (
              <div className="space-y-1.5 bg-gray-50 rounded p-2">
                {instances.length > 1 && (
                  <select
                    className={inputCls}
                    value={effectiveInstanceId}
                    onChange={(e) => setBindInstanceId(e.target.value)}
                  >
                    {instances.map((i) => (
                      <option key={i.id} value={i.id}>{i.name}（{i.url}）</option>
                    ))}
                  </select>
                )}
                {bindMode === "migrate" && (
                  <>
                    <input className={inputCls} placeholder="源仓库地址（https://github.com/user/repo.git）" value={cloneAddr} onChange={(e) => setCloneAddr(e.target.value)} />
                    <input className={inputCls} type="password" placeholder="源仓库 token（私有仓库需要，可留空）" value={migrateToken} onChange={(e) => setMigrateToken(e.target.value)} />
                  </>
                )}
                {bindMode === "link" && (
                  <input className={inputCls} placeholder="owner（Gitea 用户名或组织）" value={repoOwner} onChange={(e) => setRepoOwner(e.target.value)} />
                )}
                <input className={inputCls} placeholder="仓库名" value={repoName} onChange={(e) => setRepoName(e.target.value)} />
                <button
                  onClick={handleBind}
                  disabled={binding || !effectiveInstanceId || !repoName.trim()}
                  className="px-2.5 py-1 rounded text-[11px] bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
                >
                  {binding ? "执行中…" : "确认"}
                </button>
              </div>
            )}
          </>
        )}
      </div>

      {/* ── 全部绑定概览 ── */}
      {allLinks.length > 0 && (
        <div className="border-t border-gray-50 pt-2">
          <p className="text-[10px] text-gray-400 mb-1">全部绑定（{allLinks.length}）</p>
          <div className="space-y-0.5 max-h-24 overflow-y-auto">
            {allLinks.map((r) => (
              <div key={r.id} className="flex items-center gap-1.5 text-[10px] text-gray-500">
                <span className={`w-1 h-1 rounded-full shrink-0 ${r.has_webhook ? "bg-green-400" : "bg-gray-300"}`} title={r.has_webhook ? "webhook 已注册" : "webhook 未注册"} />
                <span className="font-mono truncate">{r.gitea_owner}/{r.gitea_repo_name}</span>
                <span className="text-gray-300 truncate ml-auto shrink-0">{r.instance_name}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
