import { useState } from "react";
import { ConfirmationModal, Dropdown, Icon } from "@nodedc/ui-react";

import {
  type ObservationSessionReplayLaunch,
  type ObservationSessionSummary,
} from "../core/observation/sessionArchive";
import { useObservationSessions } from "../core/observation/useObservationSessions";
import type {
  ObservationPreparationPhase,
  ObservationReplayOutcome,
} from "../core/observation/useObservationSessions";
import "../styles/observation-sessions.css";

type SessionVisualState = "ready" | "processing" | "cold" | "error";

export function observationSessionVisualState(
  session: ObservationSessionSummary,
  { pending = false, failed = false }: { pending?: boolean; failed?: boolean } = {},
): SessionVisualState {
  if (failed) return "error";
  if (pending) return "processing";
  if (session.preparation !== null) {
    if (["queued", "validating", "exporting", "finalizing"].includes(
      session.preparation.state,
    )) return "processing";
    if (session.preparation.state === "ready" && session.replayable) return "ready";
    return "error";
  }
  if (session.status === "recording") return "processing";
  return session.replayable ? "cold" : "error";
}

export function observationSessionVisualLabel(state: SessionVisualState): string {
  if (state === "ready") return "Готово";
  if (state === "processing") return "Обработка";
  if (state === "cold") return "Подготовить";
  return "Ошибка";
}

const modalityLabel: Record<string, string> = {
  "point-cloud": "облако точек",
  pose: "траектория",
  trajectory: "траектория",
  video: "видео",
  image: "изображения",
  depth: "глубина",
  telemetry: "телеметрия",
};

const preparationLabel: Record<ObservationPreparationPhase, string> = {
  requesting: "Запрашиваем подготовку",
  queued: "В очереди",
  validating: "Проверяем запись",
  exporting: "Готовим операторскую сцену",
  finalizing: "Завершаем подготовку",
  failed: "Подготовка не выполнена",
  cancelled: "Подготовка отменена",
};

function progressCopy(phase: ObservationPreparationPhase): string {
  // Backend progress values are phase markers and heartbeat revisions, not a
  // measured fraction of bytes or frames. Presenting 0.5 as "50%" made a
  // healthy long export look stalled, especially after a browser reload.
  return preparationLabel[phase];
}

function formatStartedAt(value: string): string {
  return new Intl.DateTimeFormat("ru-RU", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  }).format(new Date(value));
}

function formatDuration(seconds: number): string {
  const totalSeconds = Math.max(0, Math.round(seconds));
  const hours = Math.floor(totalSeconds / 3_600);
  const minutes = Math.floor((totalSeconds % 3_600) / 60);
  const remainingSeconds = totalSeconds % 60;
  return hours > 0
    ? `${hours}:${String(minutes).padStart(2, "0")}:${String(remainingSeconds).padStart(2, "0")}`
    : `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
}

function sessionDescription(session: ObservationSessionSummary): string {
  const modalities = session.modalities.length
    ? session.modalities.map((value) => modalityLabel[value] ?? value).join(" · ")
    : "каналы не зафиксированы";
  return `${formatStartedAt(session.startedAtUtc)} · ${formatDuration(session.durationSeconds)} · ${modalities}`;
}

export function ObservationSessionSelect({
  limit = 100,
  disabled = false,
  blockedReason = null,
  onReplayBegin,
  onReplayAccepted,
  onReplaySettled,
}: {
  limit?: number;
  disabled?: boolean;
  blockedReason?: string | null;
  onReplayBegin?: (
    session: ObservationSessionSummary,
    launch: ObservationSessionReplayLaunch,
  ) => void | Promise<void>;
  onReplayAccepted?: (
    session: ObservationSessionSummary,
    launch: ObservationSessionReplayLaunch,
  ) => void | Promise<void>;
  onReplaySettled?: (
    session: ObservationSessionSummary,
    outcome: ObservationReplayOutcome,
  ) => void | Promise<void>;
}) {
  const [deleteTarget, setDeleteTarget] = useState<ObservationSessionSummary | null>(null);
  const sessions = useObservationSessions({
    limit,
    replayEnabled: blockedReason === null,
    onReplayBegin,
    onReplayAccepted,
    onReplaySettled,
  });
  const triggerCopy = sessions.replayProgress
    ? progressCopy(sessions.replayProgress.phase)
    : sessions.state === "loading"
      ? "Загружаем сессии…"
      : "Сохранённые сессии";
  const presentedTriggerCopy = blockedReason ?? triggerCopy;

  return <>
    <Dropdown
      className="observation-session-select"
      placement="bottom-end"
      width={390}
      offset={10}
      disabled={disabled}
      surfaceRole="dialog"
      surfaceClassName="observation-session-menu"
      trigger={({ open, toggle, setAnchorRef, setTriggerRef, surfaceId }) => (
        <button
          ref={(node) => {
            setAnchorRef(node);
            setTriggerRef(node);
          }}
          type="button"
          className="observation-session-select__trigger"
          data-active={open ? "true" : undefined}
          aria-label="Открыть сохранённые сессии наблюдения"
          aria-haspopup="dialog"
          aria-expanded={open}
          aria-controls={surfaceId}
          disabled={disabled}
          title={blockedReason ?? undefined}
          onClick={toggle}
        >
          <Icon name="database" size={15} />
          <span>{presentedTriggerCopy}</span>
          {sessions.state === "ready" ? <small>{sessions.items.length}</small> : null}
          <Icon name="chevron-down" size={14} />
        </button>
      )}
    >
      {({ close }) => (
        <div className="observation-session-menu__content">
          <header className="observation-session-menu__head">
            <div>
              <span className="section-eyebrow">ИСТОРИЯ НАБЛЮДЕНИЯ</span>
              <strong>Сохранённые сессии</strong>
            </div>
            <button
              type="button"
              aria-label="Обновить каталог сессий"
              disabled={sessions.state === "loading"}
              onClick={() => void sessions.refresh()}
            >
              <Icon name="refresh" size={14} />
            </button>
          </header>

          {sessions.items.length > 0 ? (
            <div className="observation-session-menu__list">
              {sessions.items.map((session) => {
                const pending = sessions.replayingSessionId === session.id;
                const deleting = sessions.deletingSessionId === session.id;
                const failed = sessions.failedSessionId === session.id;
                const visualState = observationSessionVisualState(session, { pending, failed });
                return (
                  <div
                    key={session.id}
                    className="observation-session-option"
                    data-deleting={deleting ? "true" : undefined}
                  >
                    <button
                      type="button"
                      className="nodedc-dropdown-option observation-session-option__open"
                      disabled={pending || deleting || !session.replayable}
                      onClick={() => {
                        void sessions.replay(session.id).then((accepted) => {
                          if (accepted) close();
                        });
                      }}
                    >
                      <span className="nodedc-dropdown-option__icon">
                        <i data-session-visual-state={visualState} aria-hidden="true" />
                      </span>
                      <span className="nodedc-dropdown-option__body">
                        <span className="nodedc-dropdown-option__label">{session.label}</span>
                        <span className="nodedc-dropdown-option__description">
                          {sessionDescription(session)}
                        </span>
                      </span>
                      <span className="observation-session-option__state">
                        {deleting ? "Удаление…" : observationSessionVisualLabel(visualState)}
                      </span>
                    </button>
                    <button
                      type="button"
                      className="observation-session-option__delete"
                      aria-label={`Удалить сохранённую сессию ${session.label}`}
                      disabled={pending || deleting}
                      onClick={() => setDeleteTarget(session)}
                    >
                      <Icon name="trash" size={15} />
                    </button>
                  </div>
                );
              })}
            </div>
          ) : sessions.state === "loading" ? (
            <div className="observation-session-menu__empty" role="status">
              <span className="busy-indicator" aria-hidden="true" />
              <strong>Читаем каталог</strong>
            </div>
          ) : (
            <div className="observation-session-menu__empty">
              <Icon name={sessions.error ? "alert" : "database"} size={18} />
              <strong>{sessions.error ? "Каталог недоступен" : "Сессий пока нет"}</strong>
              <span>{sessions.error ?? "Завершённые записи появятся здесь автоматически."}</span>
            </div>
          )}

          {sessions.error && sessions.items.length > 0 ? (
            <footer className="observation-session-menu__error" role="alert">
              <Icon name="alert" size={13} />
              <span>{sessions.error}</span>
              {sessions.failedSessionId ? (
                <button type="button" onClick={() => void sessions.retry()}>
                  Повторить
                </button>
              ) : null}
            </footer>
          ) : null}

        </div>
      )}
    </Dropdown>
    <ConfirmationModal
      open={deleteTarget !== null}
      title="Удалить сохранённую сессию?"
      description={deleteTarget ? <>
        <strong>{deleteTarget.label}</strong>
        <p>
          Сессия, исходные данные наблюдения, подготовленная Rerun-запись и
          видеоматериалы будут удалены с этого сервера без возможности восстановления.
        </p>
        {sessions.error && sessions.deletingSessionId === null ? (
          <p className="observation-session-delete-error" role="alert">{sessions.error}</p>
        ) : null}
      </> : null}
      confirmLabel="Удалить сессию"
      pendingLabel="Удаление…"
      danger
      onClose={() => {
        if (sessions.deletingSessionId === null) setDeleteTarget(null);
      }}
      onConfirm={async () => {
        if (!deleteTarget) return;
        if (await sessions.remove(deleteTarget.id)) setDeleteTarget(null);
      }}
    />
  </>;
}
