import type { JSX, ParentComponent } from "solid-js";
import {
  For,
  Show,
  createEffect,
  createMemo,
  createResource,
  createSignal,
  onMount,
  onCleanup,
} from "solid-js";
import { A, useLocation } from "@solidjs/router";
import Dialog from "~/components/dialog/Dialog";
import { updateDebugConfig } from "~/service/debugLog";
import {
  RELEASES_PAGE_URL,
  type LatestRelease,
  type ReleaseAsset,
  type RuntimeTarget,
  detectRuntimeTarget,
  downloadReleaseAsset,
  fetchLatestRelease,
  isRemoteVersionNewer,
  resolveAssetForRuntime,
  resolveReleaseLink,
} from "~/service/update";
import { versionApi } from "~/service/wk";
import { settingsStore } from "~/store/settings";

type DownloadState = "idle" | "downloading" | "done" | "error";
type UpdateCheckState =
  | "idle"
  | "checking"
  | "latest"
  | "available"
  | "error";
type MarkdownBlock =
  | { type: "heading"; level: 1 | 2 | 3 | 4 | 5 | 6; text: string }
  | { type: "paragraph"; text: string }
  | { type: "list"; items: string[] };

const normalizeMarkdown = (value: string) => value.replace(/\r\n?/g, "\n");

const parseMarkdownBlocks = (markdown: string): MarkdownBlock[] => {
  const lines = normalizeMarkdown(markdown).split("\n");
  const blocks: MarkdownBlock[] = [];
  let index = 0;

  while (index < lines.length) {
    const line = lines[index].trim();
    if (!line) {
      index += 1;
      continue;
    }

    const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
    if (headingMatch) {
      const level = Math.min(
        6,
        Math.max(1, headingMatch[1].length),
      ) as 1 | 2 | 3 | 4 | 5 | 6;
      blocks.push({
        type: "heading",
        level,
        text: headingMatch[2].trim(),
      });
      index += 1;
      continue;
    }

    if (line.startsWith("- ")) {
      const items: string[] = [];
      while (index < lines.length) {
        const nextLine = lines[index].trim();
        if (!nextLine.startsWith("- ")) {
          break;
        }
        items.push(nextLine.slice(2).trim());
        index += 1;
      }
      blocks.push({ type: "list", items });
      continue;
    }

    const paragraphLines: string[] = [line];
    index += 1;
    while (index < lines.length) {
      const nextLine = lines[index].trim();
      if (!nextLine || nextLine.startsWith("- ") || /^#{1,6}\s+/.test(nextLine)) {
        break;
      }
      paragraphLines.push(nextLine);
      index += 1;
    }
    blocks.push({
      type: "paragraph",
      text: paragraphLines.join(" ").trim(),
    });
  }

  return blocks;
};

const renderInlineLinks = (text: string): JSX.Element[] => {
  const parts: JSX.Element[] = [];
  const regex = /\[([^\]]+)\]\(([^)]+)\)/g;
  let lastIndex = 0;
  let match: RegExpExecArray | null;

  while ((match = regex.exec(text)) !== null) {
    if (match.index > lastIndex) {
      parts.push(text.slice(lastIndex, match.index));
    }

    const label = match[1] || "链接";
    const href = resolveReleaseLink(match[2] || "");
    parts.push(
      <a
        href={href}
        target="_blank"
        rel="noopener noreferrer"
        class="text-cyan-700 underline decoration-cyan-300 underline-offset-2 hover:text-cyan-800"
      >
        {label}
      </a>,
    );
    lastIndex = regex.lastIndex;
  }

  if (lastIndex < text.length) {
    parts.push(text.slice(lastIndex));
  }

  return parts.length > 0 ? parts : [text];
};

const App: ParentComponent = (props) => {
  const location = useLocation();
  const [version] = createResource(versionApi);
  const [updateDialogOpen, setUpdateDialogOpen] = createSignal(false);
  const [updateCheckState, setUpdateCheckState] =
    createSignal<UpdateCheckState>("idle");
  const [hasAutoCheckedUpdate, setHasAutoCheckedUpdate] = createSignal(false);
  const [latestRelease, setLatestRelease] = createSignal<LatestRelease | null>(
    null,
  );
  const [runtimeTarget, setRuntimeTarget] = createSignal<RuntimeTarget>({
    os: "unknown",
    arch: "unknown",
  });
  const [matchedAsset, setMatchedAsset] = createSignal<ReleaseAsset | null>(
    null,
  );
  const [updateCheckError, setUpdateCheckError] = createSignal("");
  const [downloadState, setDownloadState] = createSignal<DownloadState>("idle");
  const [downloadProgress, setDownloadProgress] = createSignal(0);
  const [downloadError, setDownloadError] = createSignal("");
  const [settingsState, setSettingsState] = createSignal(settingsStore.getState());
  let updateAbortController: AbortController | null = null;
  let lastDebugSyncValue: boolean | undefined;

  const isActive = (url: string) =>
    location.pathname === url ||
    (location.pathname === "/" && url === "/account");

  const versionText = createMemo(() => version()?.data.Version ?? "unknown");
  const commitText = createMemo(() => {
    const commit = version()?.data.GitCommit ?? "unknown";
    return commit === "unknown" ? commit : commit.slice(0, 7);
  });
  const buildText = createMemo(() => version()?.data.BuildAt ?? "unknown");
  const authorText = createMemo(() => version()?.data.GitAuthor ?? "unknown");
  const emailText = createMemo(() => version()?.data.GitEmail ?? "unknown");
  const modeText = createMemo(() => version()?.data.Mode ?? "unknown");
  const isDebugMode = createMemo(() => settingsState().debugEnabled);
  const asideList = createMemo(() => {
    const items = [
      { label: "账号", url: "/account" },
      { label: "日志", url: "/logs" },
    ];

    if (isDebugMode()) {
      items.push({ label: "后端日志", url: "/debug-logs" });
    }

    items.push({ label: "设置", url: "/setting" });
    return items;
  });
  const versionErrorText = createMemo(() => {
    const error = version.error;
    if (!error) {
      return "";
    }

    return error instanceof Error ? error.message : "版本信息获取失败";
  });
  const releaseNotesBlocks = createMemo(() =>
    parseMarkdownBlocks(latestRelease()?.body ?? ""),
  );
  const runtimeTargetText = createMemo(
    () => `${runtimeTarget().os} / ${runtimeTarget().arch}`,
  );
  const releaseLink = createMemo(
    () => latestRelease()?.html_url || RELEASES_PAGE_URL,
  );
  const safeValue = (value: string) => (value === "unknown" ? "-" : value);
  const hasUpdateBadge = createMemo(() => updateCheckState() === "available");
  const updateDialogTitle = createMemo(() => {
    if (updateCheckState() === "available") {
      return `发现更新 ${latestRelease()?.tag_name ?? ""}`;
    }
    return "更新信息";
  });

  onMount(() => {
    const unsubscribe = settingsStore.subscribe((state) => {
      setSettingsState(state);
    });

    onCleanup(() => {
      unsubscribe();
    });
  });

  const performUpdateCheck = async (manual = false) => {
    if (updateCheckState() === "checking") {
      return;
    }

    setUpdateCheckState("checking");
    setUpdateCheckError("");
    setDownloadState("idle");
    setDownloadProgress(0);
    setDownloadError("");

    if (updateAbortController) {
      updateAbortController.abort();
    }
    updateAbortController = new AbortController();

    try {
      const [release, target] = await Promise.all([
        fetchLatestRelease(updateAbortController.signal),
        detectRuntimeTarget(),
      ]);
      setRuntimeTarget(target);

      const hasNewVersion = isRemoteVersionNewer(versionText(), release.tag_name);
      setLatestRelease(release);
      setMatchedAsset(resolveAssetForRuntime(release.assets, target));

      if (!hasNewVersion) {
        setUpdateCheckState("latest");
        if (manual) {
          setUpdateDialogOpen(true);
        }
        return;
      }

      setUpdateCheckState("available");
      setUpdateDialogOpen(true);
    } catch (error) {
      if (error instanceof DOMException && error.name === "AbortError") {
        return;
      }

      const message = error instanceof Error ? error.message : "更新检查失败";
      setUpdateCheckError(message);
      setUpdateCheckState("error");
    }
  };
  const openReleasePage = () => {
    window.open(releaseLink(), "_blank", "noopener,noreferrer");
  };
  const handleDownloadUpdate = async () => {
    const asset = matchedAsset();
    if (!asset) {
      openReleasePage();
      return;
    }

    setDownloadState("downloading");
    setDownloadProgress(0);
    setDownloadError("");
    try {
      await downloadReleaseAsset(asset, (progress) => {
        setDownloadProgress(progress);
      });
      setDownloadState("done");
    } catch (error) {
      const message = error instanceof Error ? error.message : "下载失败";
      setDownloadError(message);
      setDownloadState("error");
      openReleasePage();
    }
  };

  createEffect(() => {
    version.loading;
    if (hasAutoCheckedUpdate() || version.loading) {
      return;
    }

    setHasAutoCheckedUpdate(true);
    void performUpdateCheck(false);
  });

  createEffect(() => {
    const enabled = settingsState().debugEnabled;
    if (lastDebugSyncValue === enabled) {
      return;
    }
    lastDebugSyncValue = enabled;
    void updateDebugConfig(enabled).catch(() => {
      // Keep the local preference and let settings page surface sync errors.
    });
  });

  onCleanup(() => {
    if (updateAbortController) {
      updateAbortController.abort();
      updateAbortController = null;
    }
  });

  return (
    <>
      <div class="flex h-dvh w-full overflow-hidden bg-[radial-gradient(circle_at_top_left,_rgba(103,232,249,0.24),_transparent_28%),radial-gradient(circle_at_top_right,_rgba(251,191,36,0.16),_transparent_24%),linear-gradient(180deg,_#ecfeff_0%,_#f8fafc_52%,_#eef2ff_100%)] text-zinc-800">
        <div class="flex min-h-0 w-full flex-col p-3 sm:p-4">
          <header class="flex shrink-0 items-center rounded-[24px] border border-white/70 bg-white/75 px-4 py-3 shadow-[0_18px_50px_-22px_rgba(14,116,144,0.35)] backdrop-blur-xl">
            <div class="flex min-w-0 items-center gap-3">
              <A
                href="/"
                class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[linear-gradient(135deg,_#06b6d4,_#22c55e)] text-base font-semibold text-white shadow-lg shadow-cyan-500/20"
              >
                WK
              </A>
              <div class="min-w-0">
                <A
                  href="/"
                  class="block truncate text-xl font-semibold tracking-wide text-zinc-900"
                >
                  网课控制台
                </A>
                <p class="truncate text-xs text-zinc-500 sm:text-sm">
                  管理账号、课程记录与任务日志
                </p>
              </div>
            </div>
          </header>

          <div class="mt-4 flex min-h-0 min-w-0 flex-1 flex-col gap-4 xl:flex-row">
            <aside class="flex shrink-0 flex-col rounded-[28px] border border-white/70 bg-white/70 p-3 shadow-[0_18px_50px_-22px_rgba(15,23,42,0.18)] backdrop-blur-xl xl:w-64">
              <div class="mb-4 hidden rounded-2xl bg-[linear-gradient(135deg,_rgba(6,182,212,0.12),_rgba(34,197,94,0.14))] px-4 py-4 xl:block">
                <p class="text-xs font-medium tracking-[0.24em] text-cyan-700/75 uppercase">
                  Navigation
                </p>
                <p class="mt-2 text-sm leading-6 text-zinc-600">
                  在这里切换账号管理、全局日志与系统设置。
                </p>
              </div>

              <nav class="flex gap-2 overflow-x-auto pb-1 xl:flex-col xl:overflow-visible xl:pb-0">
                {asideList().map((item) => {
                  const active = isActive(item.url);

                  return (
                    <A
                      href={item.url}
                      class={
                        active
                          ? "min-w-fit whitespace-nowrap rounded-2xl border border-cyan-200 bg-[linear-gradient(135deg,_rgba(34,211,238,0.18),_rgba(34,197,94,0.18))] px-4 py-3 text-sm font-medium text-zinc-900 shadow-sm"
                          : "min-w-fit whitespace-nowrap rounded-2xl border border-transparent px-4 py-3 text-sm font-medium text-zinc-600 transition active:scale-[0.98] hover:border-zinc-200 hover:bg-zinc-50/80 hover:text-zinc-900"
                      }
                    >
                      <div class="flex items-center justify-between">
                        <span>{item.label}</span>
                        <span
                          class={
                            active
                              ? "text-cyan-700"
                              : "text-zinc-400 transition group-hover:text-zinc-600"
                          }
                        >
                          ›
                        </span>
                      </div>
                    </A>
                  );
                })}
              </nav>

              <div class="mt-3 rounded-2xl border border-zinc-200/80 bg-zinc-50/90 px-4 py-4 xl:mt-auto">
                <p class="text-sm font-medium text-zinc-800">当前页面</p>
                <p class="mt-1 text-sm text-zinc-500">
                  {asideList().find((item) => isActive(item.url))?.label ?? "账号"}
                </p>
                <div class="mt-3 border-t border-zinc-200/80 pt-3">
                  <button
                    type="button"
                    class="flex w-full min-w-0 items-center gap-2 rounded-lg border border-zinc-200 bg-white/80 px-2.5 py-1.5 text-left text-xs text-zinc-500 transition hover:bg-white disabled:cursor-not-allowed disabled:opacity-60"
                    disabled={updateCheckState() === "checking"}
                    onClick={() => void performUpdateCheck(true)}
                    title="点击查看更新内容"
                  >
                    <span class="text-zinc-400">版本</span>
                    <span class="shrink-0 rounded-full border border-zinc-200 bg-zinc-100 px-2 py-0.5 text-[11px] font-medium text-zinc-700">
                      {safeValue(versionText())}
                    </span>
                    {hasUpdateBadge() ? (
                      <span
                        class="h-2 w-2 shrink-0 rounded-full bg-rose-500"
                        title={`新版本：${latestRelease()?.tag_name ?? "-"}`}
                      />
                    ) : null}
                    <span class="ml-auto text-[11px] text-zinc-400">
                      {updateCheckState() === "checking" ? "检查中..." : "查看更新"}
                    </span>
                  </button>
                  <details class="mt-2 rounded-lg border border-zinc-200/80 bg-white/70 px-2.5 py-2">
                    <summary class="cursor-pointer select-none text-[11px] text-zinc-500">
                      系统诊断信息
                    </summary>
                    <div class="mt-2 space-y-1 text-xs">
                      <p class={isDebugMode() ? "text-amber-600" : "text-zinc-600"}>
                        Mode: {safeValue(modeText())}
                      </p>
                      <p class="truncate text-zinc-600">Commit: {safeValue(commitText())}</p>
                      <p class="truncate text-zinc-600">Build: {safeValue(buildText())}</p>
                      <p class="truncate text-zinc-600">Author: {safeValue(authorText())}</p>
                      <p class="truncate text-zinc-600">Email: {safeValue(emailText())}</p>
                    </div>
                  </details>
                </div>
                {updateCheckState() === "error" ? (
                  <p class="mt-2 text-xs text-rose-500">
                    {updateCheckError() || "更新检查失败"}
                  </p>
                ) : null}
                {versionErrorText() ? (
                  <p class="mt-2 text-xs text-rose-500">{versionErrorText()}</p>
                ) : null}
              </div>
            </aside>

            <main class="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-[32px] border border-white/80 bg-white/80 p-3 shadow-[0_18px_60px_-24px_rgba(15,23,42,0.22)] backdrop-blur-xl sm:p-4">
              {props.children}
            </main>
          </div>
        </div>
      </div>

      <Dialog
        open={updateDialogOpen}
        onClose={() => {
          if (downloadState() === "downloading") {
            return;
          }
          setUpdateDialogOpen(false);
        }}
        title={updateDialogTitle()}
        widthClass="max-w-3xl"
        closeOnOverlay={downloadState() !== "downloading"}
        footer={
          <>
            <button
              type="button"
              class="rounded-xl border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-700 transition active:bg-zinc-200 hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-60"
              disabled={downloadState() === "downloading"}
              onClick={() => setUpdateDialogOpen(false)}
            >
              稍后更新
            </button>
            <button
              type="button"
              class="rounded-xl border border-cyan-200 bg-cyan-50 px-3 py-2 text-sm text-cyan-700 transition active:bg-cyan-200 hover:bg-cyan-100"
              onClick={openReleasePage}
            >
              打开 Release
            </button>
            <button
              type="button"
              class="rounded-xl bg-[linear-gradient(135deg,#06b6d4,#14b8a6)] px-3 py-2 text-sm text-white transition active:scale-[0.97] hover:opacity-95 disabled:cursor-not-allowed disabled:opacity-60"
              disabled={downloadState() === "downloading"}
              onClick={() => void handleDownloadUpdate()}
            >
              {matchedAsset()
                ? downloadState() === "downloading"
                  ? `下载中 ${downloadProgress()}%`
                  : "在线下载"
                : "前往 Release 下载"}
            </button>
          </>
        }
      >
        <div class="grid gap-4">
          <div class="grid gap-2 rounded-2xl border border-zinc-200 bg-zinc-50/85 p-4 text-sm text-zinc-700 md:grid-cols-2">
            <p>
              当前版本：<span class="font-semibold text-zinc-900">{versionText()}</span>
            </p>
            <p>
              最新版本：
              <span class="font-semibold text-zinc-900">
                {latestRelease()?.tag_name ?? "-"}
              </span>
            </p>
            <p>
              发布名称：
              <span class="font-semibold text-zinc-900">
                {latestRelease()?.name ?? "-"}
              </span>
            </p>
            <p>
              运行环境：
              <span class="font-semibold text-zinc-900">{runtimeTargetText()}</span>
            </p>
            <p class="md:col-span-2">
              匹配资源：
              <span class="font-semibold text-zinc-900">
                {matchedAsset()?.name ?? "未识别当前系统架构，请点击“打开 Release”手动下载"}
              </span>
            </p>
          </div>

          <Show when={downloadState() !== "idle"}>
            <div class="rounded-2xl border border-zinc-200 bg-zinc-50/85 p-4">
              <p class="text-sm font-medium text-zinc-800">
                {downloadState() === "downloading"
                  ? "下载进度"
                  : downloadState() === "done"
                    ? "下载完成"
                    : "下载失败"}
              </p>
              <div class="mt-2 h-2.5 overflow-hidden rounded-full bg-zinc-200">
                <div
                  class={`h-full rounded-full transition-all ${downloadState() === "error" ? "bg-rose-400" : "bg-cyan-500"}`}
                  style={{
                    width: `${downloadState() === "error" ? 100 : downloadProgress()}%`,
                  }}
                />
              </div>
              <p class="mt-2 text-xs text-zinc-600">
                {downloadState() === "downloading"
                  ? `已下载 ${downloadProgress()}%`
                  : downloadState() === "done"
                    ? "安装包已下载到浏览器默认下载目录，请替换本地程序后重启。"
                    : downloadError() || "下载失败，请改为 Release 页面手动下载。"}
              </p>
            </div>
          </Show>

          <div class="rounded-2xl border border-zinc-200 bg-white p-4">
            <p class="text-sm font-semibold text-zinc-900">更新日志</p>
            <div class="mt-3 space-y-2 text-sm leading-6 text-zinc-700">
              <For each={releaseNotesBlocks()}>
                {(block) => {
                  if (block.type === "heading") {
                    return (
                      <h3
                        class={`font-semibold text-zinc-900 ${block.level <= 2 ? "text-base" : "text-sm"}`}
                      >
                        {renderInlineLinks(block.text)}
                      </h3>
                    );
                  }

                  if (block.type === "list") {
                    return (
                      <ul class="list-disc space-y-1 pl-5">
                        <For each={block.items}>
                          {(item) => <li>{renderInlineLinks(item)}</li>}
                        </For>
                      </ul>
                    );
                  }

                  return <p>{renderInlineLinks(block.text)}</p>;
                }}
              </For>
            </div>
          </div>
        </div>
      </Dialog>
    </>
  );
};

export default App;
