import { Dropdown, Icon, type IconName } from "@nodedc/ui-react";

import type {
  ObservationSourceAvailability,
  ObservationSourceDescriptor,
  ObservationSourceModality,
} from "../core/runtime/contracts";
import { MseFmp4WebSocketPlayer } from "./MseFmp4WebSocketPlayer";
import {
  RecordedFmp4Player,
  type RecordedObservationPlayback,
} from "./RecordedFmp4Player";
import type {
  RecordedAdmissionPhase,
  RecordedCameraAdmissionState,
} from "../core/observation/recordedSessionAdmission";

const sourceIcon: Record<ObservationSourceModality, IconName> = {
  "point-cloud": "globe",
  video: "video",
  image: "image",
  depth: "image",
};

const availabilityCopy: Record<ObservationSourceAvailability, string> = {
  unverified: "Не подтверждён",
  declared: "Канал объявлен",
  available: "Доступен",
  connecting: "Подключение",
  streaming: "Эфир",
  degraded: "Нестабильно",
  unavailable: "Недоступен",
  error: "Ошибка",
};

export function observationSourceStatusLabel(source: ObservationSourceDescriptor): string {
  return availabilityCopy[source.availability];
}

export function ObservationMedia({
  source,
  playback,
  prepareRecorded = true,
  recordedSessionGate = "ready",
  recordedAdmissionKey = null,
  onRecordedAdmissionChange,
}: {
  source: ObservationSourceDescriptor;
  playback?: RecordedObservationPlayback | null;
  prepareRecorded?: boolean;
  recordedSessionGate?: RecordedAdmissionPhase;
  recordedAdmissionKey?: string | null;
  onRecordedAdmissionChange?: (
    sourceId: string,
    state: RecordedCameraAdmissionState,
  ) => void;
}) {
  const sourceSelected = source.activation ? source.activation.selected : true;
  const deliveryActive = Boolean(source.delivery && sourceSelected);

  if (
    deliveryActive &&
    source.delivery?.kind === "mse-fmp4-websocket" &&
    source.modality === "video"
  ) {
    return <MseFmp4WebSocketPlayer delivery={source.delivery} label={source.label} />;
  }

  if (
    deliveryActive &&
    source.delivery?.kind === "recorded-fmp4-manifest" &&
    source.modality === "video"
  ) {
    if (!prepareRecorded) {
      return (
        <div className="observation-media__empty" role="status">
          <span className="busy-indicator" aria-hidden="true" />
          <strong>Ожидает подготовки</strong>
          <span>Канал будет проверен последовательно в рамках атомарной сессии.</span>
        </div>
      );
    }
    return (
      <RecordedFmp4Player
        source={source}
        playback={playback}
        prepare
        sessionGate={recordedSessionGate}
        admissionKey={recordedAdmissionKey}
        onAdmissionChange={(state) => onRecordedAdmissionChange?.(source.id, state)}
      />
    );
  }

  if (deliveryActive && source.delivery?.kind === "video-url" && source.modality === "video") {
    return (
      <video
        className="observation-media__asset"
        src={source.delivery.url}
        autoPlay
        muted
        playsInline
      />
    );
  }

  if (
    deliveryActive &&
    source.delivery?.kind === "image-url" &&
    (source.modality === "image" || source.modality === "depth")
  ) {
    return <img className="observation-media__asset" src={source.delivery.url} alt={source.label} />;
  }

  if (sourceSelected && source.previewUrl && source.modality === "video") {
    return (
      <video
        className="observation-media__asset"
        src={source.previewUrl}
        autoPlay
        muted
        playsInline
      />
    );
  }

  if (
    sourceSelected &&
    source.previewUrl &&
    (source.modality === "image" || source.modality === "depth")
  ) {
    return <img className="observation-media__asset" src={source.previewUrl} alt={source.label} />;
  }

  return (
    <div className="observation-media__empty">
      <Icon name={sourceIcon[source.modality]} size={20} />
      <strong>
        {source.modality === "video" && source.activation && !source.activation.selected
          ? "Источник отключён"
          : observationSourceStatusLabel(source)}
      </strong>
      <span>
        {source.modality === "video"
          ? source.activation?.controllable
            ? source.activation.selected
              ? "Повторите подключение — локальный адаптер перезапустит выбранную камеру"
              : "Канал остановлен оператором; при необходимости его можно открыть снова"
            : "Канал известен, browser-preview сейчас недоступен"
          : source.description}
      </span>
    </div>
  );
}

export function ObservationSourcePicker({
  sources,
  visibleSourceIds,
  pendingSourceIds,
  onToggle,
}: {
  sources: readonly ObservationSourceDescriptor[];
  visibleSourceIds: ReadonlySet<string>;
  pendingSourceIds?: ReadonlySet<string>;
  onToggle: (sourceId: string) => void | Promise<boolean>;
}) {
  const visibleCount = sources.filter((source) => visibleSourceIds.has(source.id)).length;

  return (
    <Dropdown
      className="scene-source-picker"
      placement="bottom-start"
      width={340}
      offset={10}
      surfaceRole="dialog"
      surfaceClassName="observation-source-menu"
      trigger={({ open, toggle, setAnchorRef, setTriggerRef, surfaceId }) => (
        <button
          ref={(node) => {
            setAnchorRef(node);
            setTriggerRef(node);
          }}
          type="button"
          className="scene-source-picker__trigger"
          data-active={open || visibleCount > 0 ? "true" : undefined}
          aria-label="Источники данных сцены"
          aria-haspopup="dialog"
          aria-expanded={open}
          aria-controls={surfaceId}
          onClick={toggle}
        >
          <Icon name="database" size={17} />
          {sources.length > 0 ? <span>{visibleCount}</span> : null}
        </button>
      )}
    >
      <header className="observation-source-menu__head">
        <div>
          <span className="section-eyebrow">НАБЛЮДЕНИЕ</span>
          <strong>Источники данных</strong>
        </div>
        <small>{sources.length ? `${visibleCount} из ${sources.length}` : "нет источников"}</small>
      </header>
      {sources.length ? (
        <div className="observation-source-menu__list">
          {sources.map((source) => {
            const selected = visibleSourceIds.has(source.id);
            const pending = pendingSourceIds?.has(source.id) ?? false;
            const canOpen = Boolean(
              selected ||
              source.modality === "point-cloud" ||
              source.previewUrl ||
              source.delivery ||
              source.activation?.controllable,
            );
            return (
              <button
                key={source.id}
                type="button"
                className="nodedc-dropdown-option observation-source-option"
                data-selected={selected ? "true" : undefined}
                aria-pressed={selected}
                disabled={pending || !canOpen}
                onClick={() => void onToggle(source.id)}
              >
                <span className="nodedc-dropdown-option__icon">
                  <Icon name={sourceIcon[source.modality]} size={16} />
                </span>
                <span className="nodedc-dropdown-option__body">
                  <span className="nodedc-dropdown-option__label">{source.label}</span>
                  <span className="nodedc-dropdown-option__description">
                    <i data-availability={source.availability} aria-hidden="true" />
                    {pending ? "Переключение" : observationSourceStatusLabel(source)} · {source.endpointLabel || source.transport}
                  </span>
                </span>
                <span className="nodedc-dropdown-option__check">
                  {selected ? <Icon name="check" size={15} /> : null}
                </span>
              </button>
            );
          })}
        </div>
      ) : (
        <div className="observation-source-menu__empty">
          <Icon name="database" size={18} />
          <strong>Каталог пока пуст</strong>
          <span>Выберите профиль устройства — его плагин опубликует доступные каналы.</span>
        </div>
      )}
      <footer className="observation-source-menu__foot">
        Каналы приходят из активного контура или выбранной сохранённой сессии; сцена не знает
        модель оборудования.
      </footer>
    </Dropdown>
  );
}
