import type { TguPlanDecision } from "../../model/tguTypes";
import type { TguPlanUi } from "../../model/timelineTypes";
import { platformLabel } from "./tguTimelineMapper";
import { statusStyle } from "./tguStatus";
import { formatLocalDateTime, parseLocal } from "../../utils/localTime";

// Бэкенд (pcp-tgu-service) ждёт решения по плану только waiting-decision-grace-minutes
// минут после его startTime; затем IssueDuePlanWorker.tryAdvancePastUnconfirmedPlan
// «молчаливо» продолжает цепочку, оставляя план в WAITING_DECISION, но решение по нему
// больше не принимается (endpoint вернёт 409). Держим значение синхронным с
// config-repo/pcp-tgu-service.yaml → tgu.planning.waiting-decision-grace-minutes.
const DECISION_GRACE_MINUTES = 30;

/** Терминально-негативные статусы, которые можно «снять с учёта» (синхронно с бэком DismissPlanUseCase). */
const DISMISSIBLE_STATUSES = new Set(["REJECTED", "START_AMBIGUOUS", "LAYIN_FAILED", "NOT_ACCEPTED", "SUPERSEDED"]);

/** Состав включений плана: сколько съёмок и сбросов. undefined — ещё грузится / неизвестно. */
export type PlanModeCounts = {
  survey: number;
  drop: number;
};

type TguPlanDetailsProps = {
  plan?: TguPlanUi;
  /** Состав включений плана для блока «Включения» (съёмки/сбросы). */
  modeCounts?: PlanModeCounts;
  /** Цепочка КА на паузе после REJECTED — показываем подсказку про ручной исправленный план. */
  chainPaused?: boolean;
  decisionInFlight: boolean;
  notice?: string;
  error?: string;
  onDecision: (planId: string, decision: TguPlanDecision, reason?: string) => void;
  /** Действия над черновиком (показываются только для status === "DRAFT"). */
  draftActionInFlight?: boolean;
  onEditDraft?: (planId: string) => void;
  onPromoteDraft?: (planId: string) => void;
  onDeleteDraft?: (planId: string) => void;
  /** Создать черновик-копию на основе этого плана (replace, окно 1:1, все включения). */
  onCreateDraftFromPlan?: (planId: string) => void;
  /** ТЕСТ: подтвердить закладку плана (AWAITING_LAYIN → LAID_IN). */
  onConfirmLayin?: (planId: string) => void;
  /** Нельзя подтвердить закладку: по КА уже заложен план со стартом в пределах ±10 минут. */
  layinConflict?: boolean;
  /** «Снять с учёта» терминально-негативный план (чтобы не красил индикатор КА). */
  onDismissPlan?: (planId: string) => void;
  /** «Вернуть на учёт» ранее снятый план. */
  onRestorePlan?: (planId: string) => void;
  onClose: () => void;
};

export function TguPlanDetails({
  plan,
  modeCounts,
  chainPaused,
  decisionInFlight,
  notice,
  error,
  onDecision,
  draftActionInFlight,
  onEditDraft,
  onPromoteDraft,
  onDeleteDraft,
  onCreateDraftFromPlan,
  onConfirmLayin,
  layinConflict,
  onDismissPlan,
  onRestorePlan,
  onClose
}: TguPlanDetailsProps) {
  if (!plan) {
    return (
      <aside className="tgu-details tgu-details--empty">
        <div className="tgu-details__placeholder">
          <span className="tgu-details__placeholder-icon" aria-hidden="true" />
          <span>Выберите plan bar на timeline.</span>
        </div>
      </aside>
    );
  }

  const style = statusStyle(plan.status);
  const duration = formatDuration(plan.endMs - plan.startMs);
  // Окно решения закрылось, если с момента старта плана прошло больше grace-периода.
  const decisionWindowClosed = Date.now() > plan.startMs + DECISION_GRACE_MINUTES * 60_000;
  // Начало плана ещё в будущем. Нужно и для закладки (план закладывается по факту старта,
  // не заранее), и для replace-черновика (после старта окно выдачи заморожено — бэк вернёт 422).
  const startInFuture = plan.startMs > Date.now();
  // «Создать черновик на основе плана» (replace, окно 1:1, все включения) доступно для
  // персистентных правимых баз, у которых старт ещё не наступил. DRAFT уже имеет свои действия;
  // PLANNED — виртуальная проекция, не база; LAID_IN — replace 1:1 запрещён (слот зафиксирован);
  // стартовавший план — окно выдачи заморожено (CreateDraftUseCase.createReplaceDraft → 422),
  // продолжение возможно только fork-forward (отдельный поток). Гард зеркалит редактор, чтобы
  // не предлагать кнопку, которая заведомо упадёт «Unprocessable Content».
  const draftableStatus =
    plan.status !== "DRAFT" && plan.status !== "PLANNED" && plan.status !== "LAID_IN";
  const canCreateDraftFromPlan = draftableStatus && startInFuture;
  // Статус позволял бы replace-черновик, но план уже стартовал — объясняем, почему кнопки нет.
  const draftBlockedByStart = draftableStatus && !startInFuture;

  const reject = () => {
    const reason = window.prompt("Reason для REJECTED", "UI_TEST_REJECTED");
    if (reason === null) return;
    onDecision(plan.planId, "REJECTED", reason);
  };

  const promoteDraft = () => {
    if (!window.confirm("Отправить черновик на выдачу? Он уйдёт в цепочку и станет рабочим после приёмки.")) return;
    onPromoteDraft?.(plan.planId);
  };

  const deleteDraft = () => {
    if (!window.confirm("Удалить черновик? Его нельзя будет восстановить.")) return;
    onDeleteDraft?.(plan.planId);
  };

  const confirmLayin = () => {
    if (!window.confirm("Подтвердить закладку плана? Он станет базой цепочки (LAID_IN). Вы уверены?")) return;
    onConfirmLayin?.(plan.planId);
  };

  const dismissed = Boolean(plan.dismissedAt);
  const canDismiss = DISMISSIBLE_STATUSES.has(plan.status);
  const dismiss = () => {
    if (!window.confirm("Снять план с учёта? Он перестанет считаться проблемой и не будет красить КА. Статус и цепочку это не меняет; можно вернуть на учёт.")) return;
    onDismissPlan?.(plan.planId);
  };

  return (
    <aside className="tgu-details">
      <div className="tgu-details__header">
        <div>
          <div className="tgu-details__eyebrow">Выбранный план</div>
          <h2>{plan.planId}</h2>
        </div>
        <button className="tgu-icon-button" type="button" onClick={onClose} aria-label="Закрыть детали">
          x
        </button>
      </div>

      <div className="tgu-details__pills">
        <div className={`tgu-status-pill ${style.className}`}>{style.label}</div>
        {dismissed && <div className="tgu-status-pill tgu-status-pill--dismissed">снят с учёта</div>}
      </div>

      {notice && <div className="tgu-alert tgu-alert--success">{notice}</div>}
      {error && <div className="tgu-alert tgu-alert--danger">{error}</div>}

      {chainPaused && (
        <div className="tgu-alert tgu-alert--warning">
          ⏸ Цепочка на паузе: план отклонён — создайте исправленный план вручную («Создать план»).
          Автоматической перевыдачи отклонённого окна не будет.
        </div>
      )}

      <dl className="tgu-details-grid">
        <Detail label="planId" value={plan.planId} mono />
        <Detail label="spacecraftId" value={plan.spacecraftId} mono />
        <Detail label="КА" value={platformLabel(plan.platform, plan.spacecraftId)} />
        <Detail label="startTime" value={formatDateTime(plan.startTime)} mono />
        <Detail label="endTime" value={formatDateTime(plan.endTime)} mono />
        <Detail label="duration" value={duration} mono />
        <Detail label="kppId" value={plan.kppId} mono />
        <Detail label="status" value={plan.status} mono />
        {plan.basePlanId && <Detail label="basePlanId" value={plan.basePlanId} mono />}
      </dl>

      <div className="tgu-details__eyebrow">Включения</div>
      <dl className="tgu-details-grid">
        <Detail label="съёмок" value={modeCounts ? String(modeCounts.survey) : "…"} mono />
        <Detail label="сбросов" value={modeCounts ? String(modeCounts.drop) : "…"} mono />
      </dl>

      {plan.status === "DRAFT" && (
        <div className="tgu-details__actions tgu-details__actions--draft">
          <button
            className="tgu-button tgu-button--primary"
            type="button"
            disabled={draftActionInFlight}
            onClick={() => onEditDraft?.(plan.planId)}
          >
            Редактировать
          </button>
          <button
            className="tgu-button tgu-button--accept"
            type="button"
            disabled={draftActionInFlight}
            onClick={promoteDraft}
          >
            Отправить
          </button>
          <button
            className="tgu-button tgu-button--reject"
            type="button"
            disabled={draftActionInFlight}
            onClick={deleteDraft}
          >
            Удалить
          </button>
        </div>
      )}

      {plan.status === "WAITING_DECISION" &&
        (decisionWindowClosed ? (
          <div className="tgu-alert tgu-alert--warning">
            Окно решения истекло: прошло больше {DECISION_GRACE_MINUTES} мин после начала плана. Система продолжила
            цепочку без подтверждения — решение по этому плану больше не принимается.
          </div>
        ) : (
          <div className="tgu-details__actions">
            <button
              className="tgu-button tgu-button--accept"
              type="button"
              disabled={decisionInFlight}
              onClick={() => onDecision(plan.planId, "ACCEPTED", "UI_TEST_ACCEPTED")}
            >
              Принять
            </button>
            <button className="tgu-button tgu-button--reject" type="button" disabled={decisionInFlight} onClick={reject}>
              Отклонить
            </button>
          </div>
        ))}

      {canCreateDraftFromPlan && onCreateDraftFromPlan && (
        <div className="tgu-details__actions">
          <button
            className="tgu-button tgu-button--primary"
            type="button"
            disabled={draftActionInFlight}
            title="Создать отдельный черновик-копию этого плана (то же окно, все включения) для правки и отправки"
            onClick={() => onCreateDraftFromPlan(plan.planId)}
          >
            Создать черновик на основе плана
          </button>
        </div>
      )}

      {draftBlockedByStart && (
        <div className="tgu-alert tgu-alert--warning">
          Черновик-копию (replace, то же окно) можно создать только до старта плана — окно
          выдачи уже заморожено. Продолжение цепочки на будущую ЗРВ (fork-forward) выполняется
          отдельно.
        </div>
      )}

      {plan.status === "AWAITING_LAYIN" && onConfirmLayin && (
        <div className="tgu-details__test-block">
          <div className="tgu-details__test-label">ТЕСТОВЫЙ ФУНКЦИОНАЛ</div>
          <button
            className="tgu-button tgu-button--accept"
            type="button"
            disabled={decisionInFlight || layinConflict || startInFuture}
            title={
              startInFuture
                ? "Начало плана ещё не наступило — подтвердить закладку можно только после его старта"
                : layinConflict
                  ? "По КА уже заложен план со стартом в пределах ±10 минут — закладка недоступна"
                  : "Сымитировать событие «план заложен» (AWAITING_LAYIN → LAID_IN)"
            }
            onClick={confirmLayin}
          >
            Подтвердить закладку
          </button>
          {startInFuture && (
            <div className="tgu-details__test-hint">
              Начало плана ещё в будущем — подтвердить закладку можно только после наступления его старта.
            </div>
          )}
          {!startInFuture && layinConflict && (
            <div className="tgu-details__test-hint">
              По КА уже есть заложенный план со стартом в пределах ±10 минут — подтвердить нельзя.
            </div>
          )}
        </div>
      )}

      {canDismiss && (
        dismissed
          ? onRestorePlan && (
              <div className="tgu-details__actions">
                <button
                  className="tgu-button"
                  type="button"
                  disabled={draftActionInFlight}
                  title="Вернуть план на учёт — снова будет считаться проблемой КА"
                  onClick={() => onRestorePlan(plan.planId)}
                >
                  Вернуть на учёт
                </button>
              </div>
            )
          : onDismissPlan && (
              <div className="tgu-details__actions">
                <button
                  className="tgu-button"
                  type="button"
                  disabled={draftActionInFlight}
                  title="Снять отменённый/неудавшийся план с учёта, чтобы он не считался проблемой и не красил КА"
                  onClick={dismiss}
                >
                  Снять с учёта
                </button>
              </div>
            )
      )}
    </aside>
  );
}

function Detail({ label, value, mono }: { label: string; value?: string; mono?: boolean }) {
  return (
    <div>
      <dt>{label}</dt>
      <dd className={mono ? "mono" : undefined}>{value || "-"}</dd>
    </div>
  );
}

function formatDateTime(value: string): string {
  return formatLocalDateTime(parseLocal(value));
}

function formatDuration(ms: number): string {
  const minutes = Math.max(0, Math.round(ms / 60_000));
  const hours = Math.floor(minutes / 60);
  const restMinutes = minutes % 60;
  if (hours === 0) return `${restMinutes} мин`;
  return `${hours} ч ${restMinutes} мин`;
}
