"use client";

import {
  useEffect,
  useState,
  Suspense,
  useMemo,
  startTransition,
} from "react";
import { useSearchParams } from "next/navigation";
import { Button, ButtonLink } from "@/components/ui/Button";
import { parseApiResponse } from "@/lib/api-client";
import type { DiffItem, SkippedRegion } from "@/lib/bump/planner";
import type { Architecture, ComponentType, Region, SectionKey } from "@/lib/types";
import BumpStepHeader from "@/components/bump/BumpStepHeader";
import BumpConfigPanel from "@/components/bump/BumpConfigPanel";
import FileSyncPanel from "@/components/bump/FileSyncPanel";
import SelectedRegionsSummary from "@/components/bump/SelectedRegionsSummary";
import { getAllowedSections } from "@/lib/bump/section-policy";
import { getStrategy } from "@/lib/bump/strategies";
import { useBumpFlow } from "./model/use-bump-flow";
import { useBumpSelection } from "./model/use-bump-selection";
import { useBumpPreview } from "./model/use-bump-preview";
import { useBumpExecute } from "./model/use-bump-execute";
import {
  buildPRMeta,
  type PRMeta,
} from "./model/pr-meta";
import { COMPONENT_OPTIONS } from "./model/component-options";
import {
  buildFileSyncPRMeta,
  getTargetRegionIdsForMode,
  isFileSyncMode,
  type BumpMode,
  type FileSyncPreviewData,
  type PerFileSourceStatus,
  type FileSyncExecuteData,
  type FileSyncDiffRow,
} from "./model/file-sync";
import type { SyncableFileName } from "@/lib/sync/file-sync/types";
import { ComponentStepSection } from "./sections/component-step";
import { PreviewStepSection } from "./sections/preview-step";
import { SelectStepSection } from "./sections/select-step";
import { ExecuteStepSection } from "./sections/execute-step";

const SECTION_LABELS: Record<SectionKey, string> = {
  k8s: "K8s",
  tidbcluster: "TidbCluster",
  sharedpool: "SharedPool",
  tidbgroup: "TidbGroup",
};

// ── Skeleton ───────────────────────────────────────────────────────

function BumpSkeleton() {
  return (
    <div className="space-y-6 animate-pulse">
      <div className="h-7 w-36 bg-surface-1 rounded" />
      <div className="flex gap-2">
        {Array.from({ length: 3 }).map((_, i) => (
          <div key={i} className="h-8 w-24 bg-surface-1 rounded-full" />
        ))}
      </div>
      <div className="space-y-3">
        <div className="h-4 w-48 bg-surface-1 rounded" />
        <div className="h-10 bg-surface-1 rounded-lg" />
      </div>
      <div className="h-10 max-w-48 bg-surface-1 rounded-lg" />
    </div>
  );
}

// ── PR Meta ────────────────────────────────────────────────────────

// ── Get available sections for selected regions ────────────────────

function getAvailableSections(regions: Region[], component: ComponentType): SectionKey[] {
  const architectureSet = new Set<Architecture>();
  for (const region of regions) {
    architectureSet.add(region.architecture);
  }

  return getAllowedSections(component, Array.from(architectureSet));
}

function MissingTargetsState() {
  return (
    <div className="space-y-4 animate-fade-in">
      <section>
        <h2 className="text-xl font-semibold tracking-tight">Bump Version</h2>
      </section>
      <section className="bg-surface-0 border border-surface-2 rounded-lg p-6 max-w-lg">
        <h3 className="text-lg font-semibold">Select regions first</h3>
        <p className="text-sm text-text-secondary mt-2">
          Choose regions on the Regions page, then return to this workflow.
        </p>
        <ButtonLink
          href="/regions"
          size="sm"
          className="mt-4"
        >
          Go to Regions
        </ButtonLink>
      </section>
    </div>
  );
}

function TargetRegionsPanel({ selectedRegions }: { selectedRegions: Region[] }) {
  const sortedSelectedRegions = [...selectedRegions].sort((a, b) =>
    a.id.localeCompare(b.id)
  );

  return (
    <div className="space-y-2">
      <SelectedRegionsSummary selectedRegions={selectedRegions} />
      <div className="bg-surface-0 border border-surface-2 rounded-lg overflow-hidden">
        <div className="px-4 py-2 border-b border-surface-2 bg-surface-1/50 text-xs font-semibold uppercase tracking-wider text-text-muted">
          Regions to change
        </div>
        <div className="max-h-56 overflow-auto divide-y divide-surface-2/60">
          {sortedSelectedRegions.map((region) => (
            <div
              key={region.id}
              className="px-4 py-2.5 flex items-center justify-between gap-4 text-sm"
            >
              <div className="min-w-0">
                <div className="font-mono text-[13px] text-text-primary truncate">
                  {region.regionName}
                </div>
                <div className="text-xs text-text-muted font-mono truncate">
                  {region.id}
                </div>
              </div>
              <div className="shrink-0 text-xs text-text-secondary font-mono">
                {region.architecture}/{region.environment}/{region.provider}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ── Inner Page ─────────────────────────────────────────────────────

function BumpPageInner() {
  const searchParams = useSearchParams();
  const preselected = useMemo(
    () => searchParams.get("regions")?.split(",").filter(Boolean) || [],
    [searchParams]
  );

  const [regions, setRegions] = useState<Region[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [mode, setMode] = useState<BumpMode>("image");
  const [component, setComponent] = useState<ComponentType | null>(null);
  const [targetTag, setTargetTag] = useState("");
  const [sourceRegionId, setSourceRegionId] = useState("");
  const [selectedFiles, setSelectedFiles] = useState<Set<SyncableFileName>>(new Set());
  const [fileStatuses, setFileStatuses] = useState<PerFileSourceStatus[]>([]);
  const [previewTokenId, setPreviewTokenId] = useState("");
  const [fileSyncSummary, setFileSyncSummary] = useState<
    FileSyncPreviewData["summary"] | null
  >(null);
  const [fileSyncDiffs, setFileSyncDiffs] = useState<FileSyncDiffRow[]>([]);
  const [fileSyncFailures, setFileSyncFailures] = useState<
    Array<{
      regionId: string;
      error: { code: string; message: string; hint?: string };
    }>
  >([]);
  const [fileSyncResult, setFileSyncResult] =
    useState<FileSyncExecuteData | null>(null);
  const {
    selectedIds,
    setSelectedIds,
  } = useBumpSelection({ preselected });
  const [selectedSections, setSelectedSections] = useState<Set<SectionKey>>(
    new Set()
  );
  const [updateVariants, setUpdateVariants] = useState(true);
  const [diffs, setDiffs] = useState<DiffItem[]>([]);
  const [skippedRegions, setSkippedRegions] = useState<SkippedRegion[]>([]);
  const [prMeta, setPrMeta] = useState<PRMeta | null>(null);
  const {
    step,
    previewing,
    bumping,
    progress,
    setStep,
    setPreviewing,
    setBumping,
    resetProgress,
    appendProgress,
  } = useBumpFlow();

  useEffect(() => {
    async function load() {
      setLoading(true);
      setError("");
      try {
        const res = await fetch("/api/regions");
        const payload = await parseApiResponse<{ regions: Region[] }>(res);
        if (payload.ok) {
          setRegions(payload.data.regions);
          if (preselected.length > 0) {
            setSelectedIds(new Set(preselected));
          }
        } else {
          setError(payload.error.message || "Failed to load regions");
        }
      } catch (e) {
        setError((e as Error).message);
      } finally {
        setLoading(false);
      }
    }
    load();
  }, [preselected, setSelectedIds]);

  useEffect(() => {
    if (component && component !== "acs") {
      const available = getAvailableSections(
        regions.filter((r) => selectedIds.has(r.id)),
        component
      );
      startTransition(() => {
        setSelectedSections(new Set(available));
      });
    }
  }, [component, regions, selectedIds]);

  useEffect(() => {
    if (!isFileSyncMode(mode) || !sourceRegionId) {
      setFileStatuses([]);
      return;
    }

    let cancelled = false;
    async function checkSourceStatus() {
      try {
        const res = await fetch(
          `/api/file-sync/source-status?sourceRegionId=${encodeURIComponent(sourceRegionId)}`
        );
        const payload = await parseApiResponse<{ sourceRegionId: string; files: PerFileSourceStatus[] }>(res);
        if (cancelled) return;

        if (payload.ok) {
          setFileStatuses(payload.data.files);
          // Auto-select files that have diff
          const autoSelected = new Set(
            payload.data.files.filter((f) => f.hasDiff).map((f) => f.sourceFile)
          );
          setSelectedFiles(autoSelected);
          return;
        }

        setFileStatuses([]);
      } catch {
        if (cancelled) return;
        setFileStatuses([]);
      }
    }

    checkSourceStatus();

    return () => {
      cancelled = true;
    };
  }, [mode, sourceRegionId]);

  const selectedRegions = regions.filter((r) => selectedIds.has(r.id));
  const selectedCount = selectedRegions.length;
  const fileSyncTargetIds = isFileSyncMode(mode)
    ? getTargetRegionIdsForMode(mode, selectedIds, sourceRegionId)
    : [];
  const effectiveSelectedCount =
    isFileSyncMode(mode) ? fileSyncTargetIds.length : selectedCount;
  const sourceOverlapsTargets =
    isFileSyncMode(mode) && Boolean(sourceRegionId) && selectedIds.has(sourceRegionId);

  const sourceHasDiff = fileStatuses.some((f) => f.hasDiff);
  const [imageTagCheckApplicable, setImageTagCheckApplicable] = useState(false);
  const [enableImageTagCheck, setEnableImageTagCheck] = useState(true);

  const handlePreview = useBumpPreview({
    mode,
    sourceRegionId,
    sourceHasDiff,
    selectedIds,
    setSelectedIds,
    fileSyncTargetIds,
    regions,
    component,
    targetTag,
    selectedSections,
    updateVariants,
    selectedRegions,
    selectedFiles,
    setPreviewing,
    setError,
    setFileSyncResult: () => setFileSyncResult(null),
    setFileSyncFailures,
    setPreviewTokenId,
    setFileSyncSummary,
    setImageTagCheckApplicable,
    setPrMeta,
    setFileSyncDiffs,
    setDiffs,
    setSkippedRegions,
    setStep,
    buildImagePRMeta: () => buildPRMeta(selectedRegions, targetTag, component!),
    buildFileSyncPRMeta,
  });

  const handleBump = useBumpExecute({
    mode,
    previewTokenId,
    prMeta,
    fileSyncSummary,
    fileSyncTargetIds,
    selectedIds,
    targetTag,
    component,
    selectedSections,
    updateVariants,
    enableImageTagCheck,
    setBumping,
    resetProgress,
    setError,
    setFileSyncResult,
    setStep,
    appendProgress,
  });

  if (loading) return <BumpSkeleton />;

  if (preselected.length === 0) return <MissingTargetsState />;

  if (error) {
    return (
      <div className="bg-danger-muted border border-danger/20 rounded-lg p-4 flex items-start gap-3">
        <div className="w-5 h-5 rounded-full bg-danger/20 flex items-center justify-center shrink-0 mt-0.5">
          <span className="text-danger text-xs font-bold">!</span>
        </div>
        <div>
          <p className="text-sm text-danger font-medium">{error}</p>
          <Button
            onClick={() => window.location.reload()}
            variant="danger"
            size="sm"
            className="mt-2"
          >
            Try again
          </Button>
        </div>
      </div>
    );
  }

  const isTagValid = targetTag.length > 0;
  const canPreview =
    isFileSyncMode(mode)
      ? Boolean(sourceRegionId) && sourceHasDiff && selectedFiles.size > 0 && effectiveSelectedCount > 0 && !previewing
      : isTagValid && selectedCount > 0 && !previewing &&
          (component === "acs" || selectedSections.size > 0);
  const availableSections = component
    ? getAvailableSections(selectedRegions, component)
    : [];

  return (
    <div className="space-y-6 animate-fade-in">
      <section>
        <h2 className="text-xl font-semibold tracking-tight">Bump Version</h2>
      </section>

      <section>
        <BumpStepHeader step={step} />
      </section>

      <section>
        <div className="inline-flex items-center p-1 rounded-xl border border-surface-2 bg-surface-0 gap-1">
          <button
            onClick={() => {
              setMode("image");
              setStep("component");
            }}
            className={`px-3 py-1.5 rounded-lg text-sm transition-colors ${
              mode === "image" ? "bg-accent/15 text-accent" : "text-text-secondary hover:bg-surface-1"
            }`}
          >
            Image Bump
          </button>
          <button
            onClick={() => {
              setMode("file-sync");
              setStep("select");
            }}
            className={`px-3 py-1.5 rounded-lg text-sm transition-colors ${
              mode === "file-sync" ? "bg-accent/15 text-accent" : "text-text-secondary hover:bg-surface-1"
            }`}
          >
            File Sync
          </button>
        </div>
      </section>

      {/* Step 0: Choose Component */}
      {step === "component" && mode === "image" && (
        <ComponentStepSection
          onSelectComponent={(componentKey) => {
            setComponent(componentKey);
            setStep("select");
          }}
        />
      )}

      {/* Step 1: Select */}
      {step === "select" && (isFileSyncMode(mode) || component) && (
        <SelectStepSection
          mode={mode}
          sourceRegionId={sourceRegionId}
          sourceStatusHasDiff={sourceHasDiff}
          sourceOverlapsTargets={sourceOverlapsTargets}
          effectiveSelectedCount={effectiveSelectedCount}
          isTagValid={isTagValid}
          targetTag={targetTag}
          selectedCount={selectedCount}
          canPreview={canPreview}
          previewing={previewing}
          onBack={() => setStep("component")}
          onPreview={handlePreview}
          targetRegionsPanel={
            <TargetRegionsPanel selectedRegions={selectedRegions} />
          }
          configPanel={
            isFileSyncMode(mode) ? (
              <FileSyncPanel
                regions={selectedRegions}
                sourceRegionId={sourceRegionId}
                onSourceRegionChange={setSourceRegionId}
                fileStatuses={fileStatuses}
                selectedFiles={selectedFiles}
                onSelectedFilesChange={setSelectedFiles}
              />
            ) : (
              component && (
                <BumpConfigPanel
                  component={component}
                  componentLabel={COMPONENT_OPTIONS.find((c) => c.key === component)?.label || ""}
                  targetTag={targetTag}
                  onTargetTagChange={setTargetTag}
                  tagPlaceholder={getStrategy(component).tagPlaceholder}
                  availableSections={availableSections}
                  selectedSections={selectedSections}
                  onToggleSection={(section) => {
                    setSelectedSections((prev) => {
                      const next = new Set(prev);
                      if (next.has(section)) next.delete(section);
                      else next.add(section);
                      return next;
                    });
                  }}
                  sectionLabels={SECTION_LABELS}
                  updateVariants={updateVariants}
                  onUpdateVariantsChange={setUpdateVariants}
                />
              )
            )
          }
        />
      )}

      {/* Step 2: Preview */}
      {step === "preview" && ((mode === "image" && prMeta) || isFileSyncMode(mode)) && (
        <PreviewStepSection
          mode={mode}
          sourceRegionId={sourceRegionId}
          prMeta={prMeta}
          targetTag={targetTag}
          selectedRegions={selectedRegions}
          diffs={diffs}
          skippedRegions={skippedRegions}
          configSyncSummary={fileSyncSummary || undefined}
          configSyncDiffs={fileSyncDiffs}
          configSyncFailures={fileSyncFailures}
          imageTagCheckApplicable={imageTagCheckApplicable}
          enableImageTagCheck={enableImageTagCheck}
          onEnableImageTagCheckChange={setEnableImageTagCheck}
          bumping={bumping}
          onBack={() => setStep("select")}
          onConfirm={handleBump}
        />
      )}

      {/* Step 3: Execute / Result */}
      {step === "execute" && (
        <ExecuteStepSection
          progress={progress}
          bumping={bumping}
          configSyncResult={fileSyncResult}
          onNewBump={() => {
            setStep("select");
            resetProgress();
            setDiffs([]);
            setSkippedRegions([]);
            setFileSyncResult(null);
            setFileSyncSummary(null);
            setFileSyncDiffs([]);
            setFileSyncFailures([]);
            setImageTagCheckApplicable(false);
            setEnableImageTagCheck(true);
          }}
        />
      )}
    </div>
  );
}

export default function BumpPage() {
  return (
    <Suspense fallback={<BumpSkeleton />}>
      <BumpPageInner />
    </Suspense>
  );
}
