"use client";

import { useState, useCallback, useMemo, useEffect, useRef } from "react";
import {
  Modal,
  Steps,
  Button,
  Space,
  Table,
  Select,
  Row,
  Col,
  Flex,
  Progress,
  Typography,
  Empty,
  Tag,
  Alert,
  App,
  theme,
} from "antd";
import { ReloadOutlined } from "@ant-design/icons";
import { useMutation } from "@tanstack/react-query";
import { useSubscription } from "@trpc/tanstack-react-query";
import type { BaseVersion, Config, CurseForgeFile, UploadReleaseType, Version } from "@/types";
import type { ParsedProject } from "@/services/project";
import { platforms } from "@/services/publish.schemas";
import type { PlatformResult, ProgressEvent } from "@/services/publish.schemas";
import { Template } from "@/lib/utils/template";
import type { McVersionEntry } from "@/lib/utils/mcVersion";
import { computeVersionRanges } from "@/lib/utils/mcVersion";
import { SemVer } from "@/lib/utils/templates/semver";
import { getModrinthMcVersions, getCurseForgeMcVersions } from "@/services/meta";
import { compareVersions } from "@/services/versions";
import { errorMessage } from "@/lib/utils/error";
import { compareParsed, parseExisting } from "@/lib/utils/existingVersion";
import { trpc } from "@/lib/trpc";
import { ChangelogEditor } from "./ChangelogEditor";

const { Text } = Typography;

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface PublishVersion {
  key: string;
  mc_version: string;
  artifact: string | null;
  sources: string | null;
  mrRange: string;
  cfRange: string;
  modrinthVersion: string;
  modrinthVersionName: string;
  existsModrinth: boolean;
  existsCurseforge: boolean;
}

/** Modrinth 表格行：待发布（kind="pending"）+ 平台已有版本（kind="existing"） */
interface ModrinthTableRow {
  key: string;
  kind: "pending" | "existing";
  pending: PublishVersion | null;
  modrinthVersion: string;
  mrRange: string;
  modrinthVersionName: string;
}

/** CurseForge 表格行 */
interface CurseforgeTableRow {
  key: string;
  kind: "pending" | "existing";
  pending: PublishVersion | null;
  mc_version: string;
  cfRange: string;
  artifact: string | null;
}

interface PublishModalProps {
  open: boolean;
  /** 配置文件名（configs/ 下的 .json 文件名） */
  configFile: string;
  config: Config;
  project: ParsedProject;
  lastVersionEnd: string | null;
  onClose: () => void;
}

type ReleaseType = Extract<BaseVersion["version_type"], UploadReleaseType>;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function entriesToMap(
  entries: McVersionEntry[] | null,
  field: "explicit" | "wildcard",
): Map<string, string> {
  const map = new Map<string, string>();
  if (entries) {
    for (const entry of entries) {
      map.set(entry.version, entry[field]);
    }
  }
  return map;
}

const RELEASE_TYPE_LABEL_MAP: Record<ReleaseType, string> = {
  release: "Release",
  beta: "Beta",
  alpha: "Alpha"
};
const RELEASE_TYPES = Object.entries(RELEASE_TYPE_LABEL_MAP).map(v => ({ value: v[0], label: v[1] }));

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export function PublishModal({
  open,
  configFile,
  config,
  project,
  lastVersionEnd,
  onClose,
}: PublishModalProps) {
  const { token } = theme.useToken();
  const { message } = App.useApp();
  const [currentStep, setCurrentStep] = useState(0);

  // Version selection
  const [selectedModrinth, setSelectedModrinth] = useState<string[]>([]);
  const [selectedCurseforge, setSelectedCurseforge] = useState<string[]>([]);

  // Publish settings
  const [changelog, setChangelog] = useState("");
  const [versionType, setVersionType] = useState<ReleaseType>(
    "release",
  );

  // Progress & result
  const [publishing, setPublishing] = useState(false);
  const [publishDone, setPublishDone] = useState(false);
  const [progress, setProgress] = useState<
    Record<
      typeof platforms[number],
      { done: number; total: number; status: ProgressEvent["status"] }
    >
  >({
    modrinth: { done: 0, total: 0, status: "running" },
    curseforge: { done: 0, total: 0, status: "running" },
  });
  const [publishResult, setPublishResult] = useState<{
    success: number;
    failed: number;
    errors: Record<typeof platforms[number], string[]>;
  } | null>(null);

  // Platform MC versions — loaded when modal opens
  const [loadingExisting, setLoadingExisting] = useState(false);
  const [modrinthMcVersions, setModrinthMcVersions] = useState<string[]>([]);
  const [curseforgeMcVersions, setCurseforgeMcVersions] = useState<string[]>([]);
  const [existingModrinth, setExistingModrinth] = useState<string[]>([]);
  const [existingCurseforge, setExistingCurseforge] = useState<string[]>([]);
  // 两平台全量已有版本（用于第一页下方灰色行展示）
  const [modrinthExistingVersions, setModrinthExistingVersions] = useState<
    Version[]
  >([]);
  const [curseforgeExistingFiles, setCurseforgeExistingFiles] = useState<
    CurseForgeFile[]
  >([]);

  const loadExisting = useCallback(async () => {
    setLoadingExisting(true);
    try {
      const mcVersions = project.artifacts.map((a) => a.mc_version);
      // 元数据列表和已有版本比对同属第一页数据，任一失败都不允许进入下一步，
      // 否则无法区分"未发布"和"没查到"，有重复发布的风险
      const [mrVersions, cfVersions, comparison] = await Promise.all([
        getModrinthMcVersions(),
        getCurseForgeMcVersions(),
        compareVersions(config, project.version, mcVersions),
      ]);
      setModrinthMcVersions(mrVersions);
      setCurseforgeMcVersions(cfVersions);
      setExistingModrinth(
        mcVersions.filter((v) => comparison.modrinth[v]?.exists),
      );
      setExistingCurseforge(
        mcVersions.filter((v) => comparison.curseforge[v]?.exists),
      );
      setModrinthExistingVersions(comparison.modrinthVersions);
      setCurseforgeExistingFiles(comparison.curseforgeFiles);
    } catch (e) {
      message.error(
        `加载已有版本失败，请检查网络后点击“刷新已有版本”重试：${errorMessage(e)}`,
      );
      // 清空比对数据，已有版本状态未知时禁用"下一步"
      setExistingModrinth([]);
      setExistingCurseforge([]);
      setModrinthExistingVersions([]);
      setCurseforgeExistingFiles([]);
    } finally {
      setLoadingExisting(false);
    }
  }, [config, project, message]);

  // Auto-load MC versions when modal opens
  useEffect(() => {
    if (open) {
      loadExisting();
    }
  }, [open, loadExisting]);

  // Pre-compute version display values
  const versionTmpl = useMemo(() => new Template(config.modrinth.version), [config.modrinth.version]);
  const versionNameTmpl = useMemo(() => new Template(config.modrinth.version_name), [config.modrinth.version_name]);

  // Compute per-platform version ranges
  const projectVersions = useMemo(
    () => project.artifacts.map((a) => a.mc_version),
    [project],
  );

  const mrRanges = useMemo(() => {
    if (modrinthMcVersions.length === 0) return null;
    return computeVersionRanges(projectVersions, modrinthMcVersions, lastVersionEnd ?? undefined);
  }, [projectVersions, modrinthMcVersions, lastVersionEnd]);

  const cfRanges = useMemo(() => {
    if (curseforgeMcVersions.length === 0) return null;
    return computeVersionRanges(projectVersions, curseforgeMcVersions, lastVersionEnd ?? undefined);
  }, [projectVersions, curseforgeMcVersions, lastVersionEnd]);

  // Build per-platform range maps (mc_version → explicit)
  const mrExplicitMap = useMemo(() => entriesToMap(mrRanges, "explicit"), [mrRanges]);
  const cfExplicitMap = useMemo(() => entriesToMap(cfRanges, "explicit"), [cfRanges]);

  // Build publish version rows from project artifacts
  const publishVersions: PublishVersion[] = useMemo(() =>
    project.artifacts.map((a) => {
      // Note: version_name template uses Modrinth explicit range for ${mc_version_range}
      const mrRange = mrExplicitMap.get(a.mc_version) ?? a.mc_version;
      const tmplValues = {
        version: project.version,
        mc_version: a.mc_version,
        mc_version_range: mrRange,
      };
      return {
        key: a.mc_version,
        mc_version: a.mc_version,
        artifact: a.artifact,
        sources: a.sources,
        mrRange: mrExplicitMap.get(a.mc_version) ?? a.mc_version,
        cfRange: cfExplicitMap.get(a.mc_version) ?? a.mc_version,
        modrinthVersion: versionTmpl.format(tmplValues),
        modrinthVersionName: versionNameTmpl.format(tmplValues),
        existsModrinth: existingModrinth.includes(a.mc_version),
        existsCurseforge: existingCurseforge.includes(a.mc_version),
      };
    }),
    [project, versionTmpl, versionNameTmpl, mrExplicitMap, cfExplicitMap, existingModrinth, existingCurseforge],
  );

  // ── 第一页表格数据源：待发布行（上）+ 平台已有版本行（下，灰色禁用）──

  // 与待发布版本匹配的已有对象不再重复出现在已有版本区
  const matchedMrNames = useMemo(
    () =>
      new Set(
        publishVersions
          .filter((v) => v.existsModrinth)
          .map((v) => v.modrinthVersion),
      ),
    [publishVersions],
  );

  const modrinthRows: ModrinthTableRow[] = useMemo(() => {
    const pending: ModrinthTableRow[] = publishVersions.map((v) => ({
      key: `pending-${v.key}`,
      kind: "pending",
      pending: v,
      modrinthVersion: v.modrinthVersion,
      mrRange: v.mrRange,
      modrinthVersionName: v.modrinthVersionName,
    }));

    const existingTmpl = new Template(config.modrinth.version);
    const existing = modrinthExistingVersions
      .filter((v) => !matchedMrNames.has(v.version_number))
      .map((v) => ({
        row: {
          key: `existing-mr-${v.version_number}`,
          kind: "existing" as const,
          pending: null,
          modrinthVersion: v.version_number,
          mrRange: "",
          modrinthVersionName: v.name,
        },
        parsed: parseExisting(existingTmpl, v.version_number),
      }))
      .sort((a, b) => compareParsed(a.parsed, b.parsed))
      .map((e) => e.row);

    return [...pending, ...existing];
  }, [publishVersions, modrinthExistingVersions, matchedMrNames, config.modrinth.version]);

  const matchedCfFileNames = useMemo(
    () =>
      new Set(
        publishVersions
          .filter((v) => v.existsCurseforge && v.artifact)
          .map((v) => v.artifact as string),
      ),
    [publishVersions],
  );

  const curseforgeRows: CurseforgeTableRow[] = useMemo(() => {
    const pending: CurseforgeTableRow[] = publishVersions.map((v) => ({
      key: `pending-${v.key}`,
      kind: "pending",
      pending: v,
      mc_version: v.mc_version,
      cfRange: v.cfRange,
      artifact: v.artifact,
    }));

    const filenameTmpl = new Template(config.filename_format);
    const existing = curseforgeExistingFiles
      .filter((f) => !matchedCfFileNames.has(f.fileName))
      .map((f) => {
        const parsed = parseExisting(filenameTmpl, f.fileName);
        return {
          row: {
            key: `existing-cf-${f.fileName}`,
            kind: "existing" as const,
            pending: null,
            mc_version: parsed.mc_version ?? "—",
            cfRange: "",
            artifact: f.displayName || f.fileName,
          },
          parsed,
        };
      })
      .sort((a, b) => compareParsed(a.parsed, b.parsed))
      .map((e) => e.row);

    return [...pending, ...existing];
  }, [publishVersions, curseforgeExistingFiles, matchedCfFileNames, config.filename_format]);

  /** 行底色：待发布且未存在 = 绿色；其余（已存在/已有版本）= 灰色 */
  const rowStyle = useCallback(
    (kind: "pending" | "existing", exists: boolean): React.CSSProperties => ({
      background:
        kind === "pending" && !exists
          ? token.colorSuccessBg
          : token.colorFillQuaternary,
    }),
    [token],
  );

  // Auto-select all non-existing versions when data first loads
  const initRef = useRef(false);
  useEffect(() => {
    if (open) {
      initRef.current = false;
    }
  }, [open]);
  useEffect(() => {
    if (!loadingExisting && publishVersions.length > 0 && !initRef.current) {
      initRef.current = true;
      setSelectedModrinth(
        publishVersions.filter((v) => !v.existsModrinth).map((v) => v.key),
      );
      setSelectedCurseforge(
        publishVersions.filter((v) => !v.existsCurseforge).map((v) => v.key),
      );
    }
  }, [loadingExisting, publishVersions]);

  // --------------- Steps handling ---------------

  // 进入第二页的前置条件：
  // 1. 数据加载完成且成功（元数据为空说明加载失败或未加载，已有版本状态未知）；
  // 2. 至少勾选一个待发布版本。
  const canProceed =
    !loadingExisting &&
    modrinthMcVersions.length > 0 &&
    curseforgeMcVersions.length > 0 &&
    (selectedModrinth.length > 0 || selectedCurseforge.length > 0);

  const handleNext = useCallback(() => {
    setCurrentStep((prev) => Math.min(prev + 1, 2));
  }, []);

  const handlePrev = useCallback(() => {
    setCurrentStep((prev) => Math.max(prev - 1, 0));
  }, []);

  const handleConfirmPublish = useCallback(() => {
    // 重置状态并进入进度页；真正发起 mutation 在下方 useSubscription 的
    // onStarted 里（保证先连上 SSE 再发布，不漏早期进度事件）
    setPublishing(true);
    setPublishDone(false);
    setPublishResult(null);
    setProgress({
      modrinth: { done: 0, total: selectedModrinth.length, status: "running" },
      curseforge: {
        done: 0,
        total: selectedCurseforge.length,
        status: "running",
      },
    });
    setCurrentStep(2);
  }, [selectedModrinth.length, selectedCurseforge.length]);

  const publishMutation = useMutation(
    trpc.publish.mutationOptions({
      onSuccess: (result) => {
        let success = 0;
        let failed = 0;
        const errors = { modrinth: [], curseforge: [] } as Record<
          typeof platforms[number],
          string[]
        >;
        for (const platform of platforms) {
          const r: PlatformResult = result[platform];
          success += r.success;
          failed += r.fail;
          errors[platform] = r.errors;
        }
        setPublishResult({ success, failed, errors });
        setPublishDone(true);
        setPublishing(false);
      },
      onError: (error) => {
        message.error(`发布请求失败：${error.message}`);
        setPublishDone(true);
        setPublishing(false);
      },
    }),
  );

  useSubscription(
    trpc.progress.subscriptionOptions(undefined, {
      enabled: publishing,
      onStarted: () => {
        // SSE 已连接，现在发起发布不会漏事件
        publishMutation.mutate({
          configFile,
          version: project.version,
          mcVersions: {
            modrinth: selectedModrinth.map((mc_version) => ({ mc_version })),
            curseforge: selectedCurseforge.map((mc_version) => ({ mc_version })),
          },
          cutoffMcVersion: lastVersionEnd ?? "",
          changelog,
          versionType,
        });
      },
      onData: (event) => {
        setProgress((prev) => ({
          ...prev,
          [event.platform]: {
            done: event.current,
            total: event.total,
            status: event.status,
          },
        }));
      },
      onError: (error) => {
        console.error("Progress subscription error:", error);
      },
    }),
  );

  const handleClose = useCallback(() => {
    if (publishing) return;
    setCurrentStep(0);
    setSelectedModrinth([]);
    setSelectedCurseforge([]);
    setChangelog("");
    setVersionType("release");
    setPublishResult(null);
    setPublishDone(false);
    onClose();
  }, [publishing, onClose]);

  // --------------- Render: Step 1 — Version Selection ---------------

  const renderStep1 = () => {
    const modrinthColumns = [
      {
        title: "版本号",
        dataIndex: "modrinthVersion",
        key: "modrinthVersion",
        width: 150,
      },
      {
        title: "兼容范围",
        dataIndex: "mrRange",
        key: "mr_range",
        width: 100,
        render: (val: string) => <Tag>{val}</Tag>,
      },
      {
        title: "版本名称",
        dataIndex: "modrinthVersionName",
        key: "modrinthVersionName",
      },
    ];

    const curseforgeColumns = [
      {
        title: "MC 版本",
        dataIndex: "mc_version",
        key: "mc_version",
        width: 80,
      },
      {
        title: "兼容范围",
        dataIndex: "cfRange",
        key: "cf_range",
        width: 100,
        render: (val: string) => <Tag>{val}</Tag>,
      },
      {
        title: "显示名称",
        dataIndex: "artifact",
        key: "artifact",
        ellipsis: true,
      },
    ];

    return (
      <div>
        <div
          style={{
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            marginBottom: token.margin,
          }}
        >
          <Text strong>选择要发布的版本</Text>
          <Button
            icon={<ReloadOutlined />}
            loading={loadingExisting}
            onClick={loadExisting}
            size="small"
          >
            刷新已有版本
          </Button>
        </div>

        {loadingExisting ? (
          <div style={{ textAlign: "center", padding: 40 }}>加载中…</div>
        ) : (
          <Row gutter={token.margin}>
            <Col span={14}>
              <Text strong style={{ display: "block", marginBottom: token.marginXS }}>
                Modrinth
              </Text>
              {modrinthRows.length > 0 ? (
                <Table
                  dataSource={modrinthRows}
                  columns={modrinthColumns}
                  rowKey="key"
                  onRow={(r) => ({
                    style: rowStyle(
                      r.kind,
                      r.kind === "pending"
                        ? (r.pending?.existsModrinth ?? false)
                        : true,
                    ),
                  })}
                  rowSelection={{
                    selectedRowKeys: selectedModrinth,
                    onChange: (keys) => setSelectedModrinth(keys as string[]),
                    getCheckboxProps: (r: ModrinthTableRow) => ({
                      disabled:
                        r.kind === "existing" ||
                        (r.pending?.existsModrinth ?? false),
                    }),
                  }}
                  pagination={false}
                  scroll={{ y: 480 }}
                  size="small"
                  bordered
                />
              ) : (
                <Empty description="无版本" />
              )}
            </Col>
            <Col span={10}>
              <Text strong style={{ display: "block", marginBottom: token.marginXS }}>
                CurseForge
              </Text>
              {curseforgeRows.length > 0 ? (
                <Table
                  dataSource={curseforgeRows}
                  columns={curseforgeColumns}
                  rowKey="key"
                  onRow={(r) => ({
                    style: rowStyle(
                      r.kind,
                      r.kind === "pending"
                        ? (r.pending?.existsCurseforge ?? false)
                        : true,
                    ),
                  })}
                  rowSelection={{
                    selectedRowKeys: selectedCurseforge,
                    onChange: (keys) => setSelectedCurseforge(keys as string[]),
                    getCheckboxProps: (r: CurseforgeTableRow) => ({
                      disabled:
                        r.kind === "existing" ||
                        (r.pending?.existsCurseforge ?? false),
                    }),
                  }}
                  pagination={false}
                  scroll={{ y: 480 }}
                  size="small"
                  bordered
                />
              ) : (
                <Empty description="无版本" />
              )}
            </Col>
          </Row>
        )}
      </div>
    );
  };

  // --------------- Render: Step 2 — Publish Settings ---------------

  const renderStep2 = () => {
    return (
      <div>
        {/* Changelog */}
        <div style={{ marginBottom: token.marginLG }}>
          <Text strong style={{ display: "block", marginBottom: token.marginSM }}>
            Changelog (Markdown)
          </Text>
          <ChangelogEditor value={changelog} onChange={setChangelog} />
        </div>

        {/* Version type */}
        <div>
          <Text strong style={{ display: "block", marginBottom: token.marginSM }}>
            版本类型
          </Text>
          <Select
            value={versionType}
            onChange={(v) => setVersionType(v)}
            style={{ width: 160 }}
            options={RELEASE_TYPES}
          />
        </div>
      </div>
    );
  };

  // --------------- Render: Step 3 — Progress & Result ---------------

  const renderStep3 = () => {
    const hasModrinth = selectedModrinth.length > 0;
    const hasCurseforge = selectedCurseforge.length > 0;

    return (
      <div>
        {/* Progress bars */}
        <div style={{ marginBottom: token.marginLG }}>
          <Flex vertical gap={token.marginSM}>
            {hasModrinth && (
              <Flex align="center" gap={token.marginXS}>
                <img
                  src="/platform/modrinth.svg"
                  alt="Modrinth"
                  style={{ width: 30, height: 30, flexShrink: 0 }}
                />
                <Text style={{ width: 80, flexShrink: 0, fontFamily: "Inter", fontWeight: "bold" }}>Modrinth</Text>
                <Progress
                  percent={Math.round(
                    (progress.modrinth.done /
                      Math.max(progress.modrinth.total, 1)) *
                      100,
                  )}
                  status={
                    progress.modrinth.status === "failed"
                      ? "exception"
                      : undefined
                  }
                  format={() =>
                    `${progress.modrinth.done} / ${progress.modrinth.total}`
                  }
                  style={{ flex: 1 }}
                />
              </Flex>
            )}
            {hasCurseforge && (
              <Flex align="center" gap={token.marginXS}>
                <img
                  src="/platform/curseforge.svg"
                  alt="CurseForge"
                  style={{ width: 30, height: 30, flexShrink: 0 }}
                />
                <Text style={{ width: 80, flexShrink: 0, fontFamily: "Inter", fontWeight: "bold" }}>CurseForge</Text>
                <Progress
                  percent={Math.round(
                    (progress.curseforge.done /
                      Math.max(progress.curseforge.total, 1)) *
                      100,
                  )}
                  status={
                    progress.curseforge.status === "failed"
                      ? "exception"
                      : undefined
                  }
                  format={() =>
                    `${progress.curseforge.done} / ${progress.curseforge.total}`
                  }
                  style={{ flex: 1 }}
                />
              </Flex>
            )}
          </Flex>
        </div>

        {/* Result */}
        {publishDone && publishResult && (
          <Alert
            type={publishResult.failed > 0 ? "warning" : "success"}
            title="发布结果"
            description={
              <div>
                <div>
                  成功 {publishResult.success} 个，失败{" "}
                  {publishResult.failed} 个
                </div>
                {platforms.map((platform) => {
                  const errors = publishResult.errors[platform];
                  if (errors.length === 0) return null;
                  const label =
                    platform === "modrinth" ? "Modrinth" : "CurseForge";
                  return (
                    <div key={platform} style={{ marginTop: token.marginSM }}>
                      <Text strong>{label}:</Text>
                      <ul style={{ margin: "4px 0 0", paddingLeft: 20 }}>
                        {errors.map((e, i) => (
                          <li key={i}>
                            <Text type="danger">{e}</Text>
                          </li>
                        ))}
                      </ul>
                    </div>
                  );
                })}
              </div>
            }
            showIcon
          />
        )}
      </div>
    );
  };

  // --------------- Render ---------------

  return (
    <Modal
      title="一键发布"
      open={open}
      onCancel={handleClose}
      width={1200}
      destroyOnHidden
      footer={
        publishing ? null : (
          <Space>
            {currentStep > 0 && currentStep < 2 && (
              <Button onClick={handlePrev}>上一步</Button>
            )}
            {currentStep === 0 ? (
              <Button
                type="primary"
                onClick={handleNext}
                disabled={!canProceed}
              >
                下一步
              </Button>
            ) : currentStep === 1 ? (
              <Button type="primary" onClick={handleConfirmPublish}>
                确认发布
              </Button>
            ) : (
              <Button type="primary" onClick={handleClose}>
                关闭
              </Button>
            )}
          </Space>
        )
      }
    >
      <Steps
        current={currentStep}
        size="small"
        style={{ marginBottom: 24 }}
        items={[
          { title: "版本选择" },
          { title: "发布设置" },
          { title: "发布进度" },
        ]}
      />

      {currentStep === 0
        ? renderStep1()
        : currentStep === 1
          ? renderStep2()
          : renderStep3()}
    </Modal>
  );
}
