import { useCallback, useEffect, useMemo, useState } from "react";
import { fetchPlans, fetchPlatforms, sendPlanDecision } from "../../api/tguApi";
import type { TguPlanDecision, TguPlatform } from "../../model/tguTypes";
import type { TguActiveTab, TguPlanUi, TimelineRange } from "../../model/timelineTypes";
import { TguEditorTab } from "../tgu-editor/TguEditorTab";
import { Tgu2DMapTab } from "../tgu-map-2d/Tgu2DMapTab";
import { TguRequestsTab } from "../tgu-requests/TguRequestsTab";
import { TguPlanDetails } from "./TguPlanDetails";
import { TguPlanningLayout } from "./TguPlanningLayout";
import { TguSidebar } from "./TguSidebar";
import { TguTimeline } from "./TguTimeline";
import { TguToolbar } from "./TguToolbar";
import { isActivePlanStatus } from "./tguStatus";
import { buildPlatformsForInterval, buildTimelineRows, mapPlans } from "./tguTimelineMapper";

const DAY_MS = 24 * 60 * 60 * 1000;

export function TguPlanningPage() {
  const initialRange = useMemo(defaultRange, []);
  const [fromValue, setFromValue] = useState(formatDateTimeLocal(initialRange.fromMs));
  const [toValue, setToValue] = useState(formatDateTimeLocal(initialRange.toMs));
  const [appliedRange, setAppliedRange] = useState<TimelineRange>(initialRange);
  const [rawPlatforms, setRawPlatforms] = useState<TguPlatform[]>([]);
  const [plans, setPlans] = useState<TguPlanUi[]>([]);
  const [loading, setLoading] = useState(false);
  const [pageError, setPageError] = useState<string>();
  const [platformLoadFailed, setPlatformLoadFailed] = useState(false);
  const [search, setSearch] = useState("");
  const [selectedSpacecraftId, setSelectedSpacecraftId] = useState<string>();
  const [selectedPlanId, setSelectedPlanId] = useState<string>();
  const [activeTab, setActiveTab] = useState<TguActiveTab>("timeline");
  // По умолчанию на таймлайне видна только актуальная цепочка; история версий
  // (SUPERSEDED/EXPIRED/REJECTED и т.п.) скрыта, чтобы не было визуальной каши.
  const [showHistory, setShowHistory] = useState(false);
  // Вкладки рендерятся в режиме keep-alive: каждую монтируем при первом открытии
  // и дальше держим смонтированной (скрытой через display:none), чтобы её
  // внутреннее состояние — загруженные заявки, выбор, слои, скролл — не сбрасывалось
  // при переключении между вкладками.
  const [visitedTabs, setVisitedTabs] = useState<ReadonlySet<TguActiveTab>>(() => new Set(["timeline"]));
  const [decisionInFlight, setDecisionInFlight] = useState(false);
  const [decisionNotice, setDecisionNotice] = useState<string>();
  const [decisionError, setDecisionError] = useState<string>();

  const parsedInputRange = useMemo(
    () => ({
      fromMs: new Date(fromValue).getTime(),
      toMs: new Date(toValue).getTime()
    }),
    [fromValue, toValue]
  );
  const invalidRange =
    !Number.isFinite(parsedInputRange.fromMs) ||
    !Number.isFinite(parsedInputRange.toMs) ||
    parsedInputRange.fromMs >= parsedInputRange.toMs;

  const loadData = useCallback(
    async (range: TimelineRange) => {
      setLoading(true);
      setPageError(undefined);
      setDecisionError(undefined);

      const historyDays = historyDaysForRange(range);
      const [platformResult, planResult] = await Promise.allSettled([
        fetchPlatforms(),
        fetchPlans(historyDays)
      ]);

      if (platformResult.status === "rejected") {
        setPlatformLoadFailed(true);
      } else {
        setPlatformLoadFailed(false);
        setRawPlatforms(platformResult.value);
      }

      if (planResult.status === "rejected") {
        setPlans([]);
        setPageError(planResult.reason instanceof Error ? planResult.reason.message : "Не удалось загрузить планы.");
      } else {
        const platforms = platformResult.status === "fulfilled" ? platformResult.value : [];
        setPlans(mapPlans(planResult.value, platforms));
      }

      setLoading(false);
    },
    []
  );

  useEffect(() => {
    void loadData(appliedRange);
  }, [appliedRange, loadData]);

  useEffect(() => {
    setVisitedTabs((prev) => (prev.has(activeTab) ? prev : new Set(prev).add(activeTab)));
  }, [activeTab]);

  // display:contents для активной вкладки сохраняет grid-раскладку shell
  // (внутренний <main> остаётся прямым grid-элементом), display:none скрывает остальные.
  const tabStyle = (tab: TguActiveTab) =>
    ({ display: activeTab === tab ? "contents" : "none" }) as const;

  const platforms = useMemo(
    () => buildPlatformsForInterval(rawPlatforms, plans, appliedRange),
    [rawPlatforms, plans, appliedRange]
  );
  // Планы, показываемые на таймлайне: по умолчанию только актуальная цепочка.
  const timelinePlans = useMemo(
    () => (showHistory ? plans : plans.filter((plan) => isActivePlanStatus(plan.status))),
    [plans, showHistory]
  );
  const rows = useMemo(
    () => buildTimelineRows(timelinePlans, platforms, appliedRange, selectedSpacecraftId),
    [timelinePlans, platforms, appliedRange, selectedSpacecraftId]
  );
  const selectedPlan = useMemo(() => {
    const plan = plans.find((item) => item.planId === selectedPlanId);
    if (!plan) return undefined;
    const platform = platforms.find((item) => item.spacecraftId === plan.spacecraftId);
    return { ...plan, platform };
  }, [plans, platforms, selectedPlanId]);

  const applyRange = () => {
    setDecisionNotice(undefined);
    if (invalidRange) {
      setPageError("Некорректный интервал: from должен быть меньше to.");
      return;
    }
    setPageError(undefined);
    setAppliedRange(parsedInputRange);
  };

  const quickRange = (hours: number) => {
    const fromMs = startOfCurrentDayMs();
    const toMs = fromMs + hours * 60 * 60 * 1000;
    setFromValue(formatDateTimeLocal(fromMs));
    setToValue(formatDateTimeLocal(toMs));
    setAppliedRange({ fromMs, toMs });
    setPageError(undefined);
    setDecisionNotice(undefined);
  };

  const handleDecision = async (planId: string, decision: TguPlanDecision, reason?: string) => {
    setDecisionInFlight(true);
    setDecisionError(undefined);
    setDecisionNotice(undefined);

    try {
      await sendPlanDecision(planId, decision, reason);
      setDecisionNotice(
        decision === "REJECTED"
          ? "Решение REJECTED отправлено. План может вернуться в PLANNED для повторной выдачи."
          : "Решение ACCEPTED отправлено."
      );
      await loadData(appliedRange);
    } catch (error) {
      setDecisionError(error instanceof Error ? error.message : "Не удалось отправить решение.");
    } finally {
      setDecisionInFlight(false);
    }
  };

  return (
    <TguPlanningLayout
      toolbar={
        <TguToolbar
          fromValue={fromValue}
          toValue={toValue}
          loading={loading}
          activeTab={activeTab}
          error={pageError}
          onTabChange={setActiveTab}
          onFromChange={setFromValue}
          onToChange={setToValue}
          onApply={applyRange}
          onQuickRange={quickRange}
        />
      }
    >
      <div style={tabStyle("timeline")}>
        <main className="tgu-workspace">
          <TguSidebar
            platforms={platforms}
            selectedSpacecraftId={selectedSpacecraftId}
            search={search}
            platformLoadFailed={platformLoadFailed}
            onSearchChange={setSearch}
            onSelectSpacecraft={(spacecraftId) => {
              setSelectedSpacecraftId(spacecraftId);
              setSelectedPlanId(undefined);
              setDecisionNotice(undefined);
            }}
          />
          <section className="tgu-center">
            <div className="tgu-timeline-controls">
              <label className="tgu-timeline-controls__toggle">
                <input
                  type="checkbox"
                  checked={showHistory}
                  onChange={(event) => setShowHistory(event.target.checked)}
                />
                Показать историю версий (SUPERSEDED, EXPIRED и др.)
              </label>
            </div>
            <TguTimeline
              rows={rows}
              range={appliedRange}
              invalidRange={invalidRange}
              selectedPlanId={selectedPlanId}
              onSelectPlan={(planId) => {
                setSelectedPlanId(planId);
                const plan = plans.find((item) => item.planId === planId);
                if (plan) {
                  setSelectedSpacecraftId(plan.spacecraftId);
                }
              }}
            />
          </section>
          <TguPlanDetails
            plan={selectedPlan}
            decisionInFlight={decisionInFlight}
            notice={decisionNotice}
            error={decisionError}
            onDecision={handleDecision}
            onClose={() => setSelectedPlanId(undefined)}
          />
        </main>
      </div>

      {visitedTabs.has("map") && (
        <div style={tabStyle("map")}>
          <Tgu2DMapTab
            range={appliedRange}
            invalidRange={invalidRange}
            selectedPlan={selectedPlan}
            platforms={platforms}
          />
        </div>
      )}

      {visitedTabs.has("requests") && (
        <div style={tabStyle("requests")}>
          <TguRequestsTab
            range={appliedRange}
            invalidRange={invalidRange}
            platforms={platforms}
          />
        </div>
      )}

      {visitedTabs.has("editor") && (
        <div style={tabStyle("editor")}>
          <TguEditorTab
            selectedSpacecraftId={selectedPlan?.spacecraftId ?? selectedSpacecraftId}
            selectedPlan={selectedPlan}
            appliedRange={appliedRange}
            platforms={platforms}
          />
        </div>
      )}
    </TguPlanningLayout>
  );
}

function defaultRange(): TimelineRange {
  const fromMs = startOfCurrentDayMs();
  return { fromMs, toMs: fromMs + DAY_MS };
}

function startOfCurrentDayMs(): number {
  const now = new Date();
  return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
}

function formatDateTimeLocal(ms: number): string {
  const date = new Date(ms);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${year}-${month}-${day}T${hours}:${minutes}`;
}

function historyDaysForRange(range: TimelineRange): number {
  const daysBack = Math.ceil(Math.max(0, Date.now() - range.fromMs) / DAY_MS);
  return Math.max(1, daysBack + 1);
}
