"use client";

import { useEffect, useRef, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import clsx from "clsx";
import type { BossEventName } from "@/lib/boss-events";
import type { SkillInventoryDeviceGroup } from "@/lib/boss-projections";
import {
  clearNativeSessionSnapshot,
  currentAppLocation,
  isNativeBossApp,
  persistNativeSessionSnapshot,
  popAppHistoryEntry,
  pushAppHistoryEntry,
  readNativeSessionSnapshot,
  resolveAppBackAction,
} from "@/lib/boss-app-client";

export async function sendAppLog(payload: {
  deviceId: string;
  projectId?: string;
  level: "info" | "warn" | "error";
  category: string;
  message: string;
  detail?: string;
  mirrorToMaster?: boolean;
}) {
  try {
    await fetch("/api/v1/app-logs", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
      keepalive: true,
    });
  } catch {
    // Ignore client-side log transport errors.
  }
}

type AuthSessionPayload = {
  account: string;
  displayName: string;
  expiresAt: string;
  restoreToken?: string;
};

function currentProjectId(pathname: string) {
  const match = pathname.match(/^\/conversations\/([^/]+)/);
  return match?.[1];
}

function basePathname(pathname: string) {
  return pathname.split("?")[0]?.split("#")[0] || "/";
}

async function fetchNativeAwareSession() {
  const response = await fetch("/api/auth/session", {
    cache: "no-store",
    headers: { "x-boss-native-app": "1" },
  });
  if (!response.ok) return null;
  const result = (await response.json()) as { ok: boolean; session?: AuthSessionPayload };
  return result.ok ? (result.session ?? null) : null;
}

async function persistSessionFromPayload(session?: AuthSessionPayload | null) {
  if (!session?.restoreToken) return;
  await persistNativeSessionSnapshot({
    restoreToken: session.restoreToken,
    account: session.account,
    displayName: session.displayName,
    expiresAt: session.expiresAt,
    lastSyncedAt: new Date().toISOString(),
  });
}

export function AppLogBridge({
  deviceId,
}: {
  deviceId?: string;
}) {
  const pathname = usePathname();

  useEffect(() => {
    if (!deviceId) return;
    void sendAppLog({
      deviceId,
      level: "info",
      category: "app.lifecycle.ready",
      message: "APP 客户端已连接到日志桥。",
      mirrorToMaster: false,
    });
  }, [deviceId]);

  useEffect(() => {
    if (!deviceId) return;
    void sendAppLog({
      deviceId,
      projectId: currentProjectId(pathname),
      level: "info",
      category: "navigation.route_changed",
      message: pathname,
      mirrorToMaster: false,
    });
  }, [deviceId, pathname]);

  useEffect(() => {
    if (!deviceId) return;

    const onError = (event: ErrorEvent) => {
      void sendAppLog({
        deviceId,
        projectId: currentProjectId(pathname),
        level: "error",
        category: "runtime.error",
        message: event.message || "未知前端错误",
        detail: event.filename ? `${event.filename}:${event.lineno}` : undefined,
        mirrorToMaster: true,
      });
    };

    const onRejection = (event: PromiseRejectionEvent) => {
      void sendAppLog({
        deviceId,
        projectId: currentProjectId(pathname),
        level: "error",
        category: "runtime.unhandled_rejection",
        message: "捕获到未处理 Promise 异常。",
        detail: String(event.reason ?? "unknown"),
        mirrorToMaster: true,
      });
    };

    window.addEventListener("error", onError);
    window.addEventListener("unhandledrejection", onRejection);

    return () => {
      window.removeEventListener("error", onError);
      window.removeEventListener("unhandledrejection", onRejection);
    };
  }, [deviceId, pathname]);

  return null;
}

export function NativeAppBridge() {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const restoreInFlightRef = useRef(false);
  const currentPath = searchParams.toString() ? `${pathname}?${searchParams.toString()}` : pathname;

  useEffect(() => {
    pushAppHistoryEntry(currentPath);
  }, [currentPath]);

  useEffect(() => {
    let cancelled = false;

    async function reconcileSession() {
      const isNative = await isNativeBossApp();
      if (!isNative || restoreInFlightRef.current) {
        return;
      }

      const activeSession = await fetchNativeAwareSession().catch(() => null);
      if (cancelled) return;

      if (activeSession?.restoreToken) {
        await persistSessionFromPayload(activeSession);
        return;
      }

      const stored = await readNativeSessionSnapshot();
      if (cancelled || !stored?.restoreToken) return;

      if (Date.parse(stored.expiresAt) <= Date.now()) {
        await clearNativeSessionSnapshot();
        return;
      }

      restoreInFlightRef.current = true;
      try {
        const response = await fetch("/api/auth/restore", {
          method: "POST",
          headers: { "Content-Type": "application/json", "x-boss-native-app": "1" },
          body: JSON.stringify({ restoreToken: stored.restoreToken }),
        });
        if (!response.ok) {
          await clearNativeSessionSnapshot();
          return;
        }
        const result = (await response.json()) as { ok: boolean; session?: AuthSessionPayload };
        if (!result.ok || !result.session?.restoreToken) {
          await clearNativeSessionSnapshot();
          return;
        }
        await persistSessionFromPayload(result.session);
        if (basePathname(pathname).startsWith("/auth")) {
          popAppHistoryEntry(currentPath);
          router.replace("/conversations", { scroll: false });
          return;
        }
        router.refresh();
      } finally {
        restoreInFlightRef.current = false;
      }
    }

    void reconcileSession();
    return () => {
      cancelled = true;
    };
  }, [currentPath, pathname, router]);

  useEffect(() => {
    let remove: (() => void) | undefined;
    let disposed = false;

    async function bindBackHandler() {
      if (!(await isNativeBossApp())) return;
      const { App } = await import("@capacitor/app");
      if (disposed) return;

      const listener = await App.addListener("backButton", () => {
        const location = currentAppLocation();
        const action = resolveAppBackAction(location);
        if (action.mode === "history") {
          popAppHistoryEntry(location);
          router.back();
          return;
        }
        if (action.mode === "replace") {
          popAppHistoryEntry(location);
          router.replace(action.target, { scroll: false });
        }
      });

      remove = () => {
        void listener.remove();
      };
    }

    void bindBackHandler();
    return () => {
      disposed = true;
      remove?.();
    };
  }, [router, currentPath]);

  return null;
}

export function RealtimeRefresh({
  events,
  projectId,
}: {
  events: BossEventName[];
  projectId?: string;
}) {
  const router = useRouter();

  useEffect(() => {
    const source = new EventSource("/api/v1/events");
    const listeners = Array.from(new Set([
      "conversation.context_indicator.updated",
      "project.context_risk.updated",
      ...events,
    ]));
    const shouldRefresh = (event: Event) => {
      if (!projectId || !("data" in event) || typeof event.data !== "string" || !event.data.trim()) {
        return true;
      }
      try {
        const payload = JSON.parse(event.data) as { projectId?: string };
        if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
          return true;
        }
        if (typeof payload.projectId !== "string" || !payload.projectId.trim()) {
          return true;
        }
        return payload.projectId === projectId;
      } catch {
        return true;
      }
    };
    const listenerMap = new Map<string, (event: Event) => void>();

    for (const event of listeners) {
      const refresh = (nextEvent: Event) => {
        if (!shouldRefresh(nextEvent)) {
          return;
        }
        router.refresh();
      };
      listenerMap.set(event, refresh);
      source.addEventListener(event, refresh);
    }

    return () => {
      for (const event of listeners) {
        const refresh = listenerMap.get(event);
        if (refresh) {
          source.removeEventListener(event, refresh);
        }
      }
      source.close();
    };
  }, [events, projectId, router]);

  return null;
}

export function SkillInventoryPanel({
  groups,
  boundDeviceId,
}: {
  groups: SkillInventoryDeviceGroup[];
  boundDeviceId?: string;
}) {
  const [message, setMessage] = useState("");

  async function copyInvocation(skill: SkillInventoryDeviceGroup["skills"][number]) {
    try {
      await navigator.clipboard.writeText(skill.invocation);
      setMessage(`已复制 ${skill.name} 的调用语句。`);
    } catch {
      setMessage(`复制 ${skill.name} 失败，请手动复制。`);
      return;
    }
    if (boundDeviceId) {
      void sendAppLog({
        deviceId: boundDeviceId,
        level: "info",
        category: "skill.copy_invocation",
        message: `已复制 Skill：${skill.name}`,
        detail: skill.invocation,
        mirrorToMaster: true,
      });
    }
  }

  return (
    <div className="space-y-4">
      <div className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4 text-[13px] leading-6 text-[#57606A]">
        技能按绑定设备分类展示。每台电脑只显示自己已同步上来的 Skill，点击即可一键复制调用语句。
      </div>
      {groups.map((group) => (
        <div key={group.device.id} className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4">
          <div className="flex items-center justify-between gap-3">
            <div>
              <div className="text-[16px] font-semibold text-[#111111]">{group.device.name}</div>
              <div className="mt-1 text-[12px] text-[#8C8C8C]">
                {group.device.account} · {group.skills.length} 个 Skill
              </div>
            </div>
            <span
              className={clsx(
                "rounded-full px-3 py-1 text-[12px] font-semibold",
                group.device.id === boundDeviceId ? "bg-[#EAF7F0] text-[#215B39]" : "bg-[#F5F5F7] text-[#57606A]",
              )}
            >
              {group.device.id === boundDeviceId ? "当前绑定设备" : "已同步设备"}
            </span>
          </div>

          <div className="mt-4 space-y-3">
            {group.skills.map((skill) => (
              <div key={skill.skillId} className="rounded-2xl bg-[#F7F8FA] px-4 py-4">
                <div className="flex items-start justify-between gap-3">
                  <div>
                    <div className="text-[14px] font-semibold text-[#111111]">{skill.name}</div>
                    <div className="mt-1 text-[12px] text-[#8C8C8C]">{skill.path}</div>
                  </div>
                  <button
                    type="button"
                    onClick={() => void copyInvocation(skill)}
                    className="rounded-full bg-[#07C160] px-3 py-2 text-[12px] font-semibold text-white"
                  >
                    复制调用
                  </button>
                </div>
                <div className="mt-2 text-[13px] leading-6 text-[#57606A]">{skill.description}</div>
                <div className="mt-2 text-[12px] text-[#8C8C8C]">调用语句：{skill.invocation}</div>
              </div>
            ))}
          </div>
        </div>
      ))}
      {message ? (
        <div className="rounded-2xl bg-[#EAF7F0] px-4 py-3 text-[12px] text-[#215B39]">{message}</div>
      ) : null}
      {!groups.length ? (
        <div className="rounded-2xl bg-[#FFF7E6] px-4 py-4 text-[13px] leading-6 text-[#D46B08]">
          当前还没有同步到任何 Skill。请先保证本机 local-agent 在线，并已扫描到 `~/.codex/skills`。
        </div>
      ) : null}
    </div>
  );
}
