import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ChevronLeft, ChevronRight, History, Search, TriangleAlert } from 'lucide-react';
import { Button, Card, IconButton, Input, Select, Spinner } from '../components';
import {
  getPlayoutBlockHistory,
  getPlayoutBlocks,
  getPlayoutHistoryDetails,
  getPlayouts,
  messageFromPlayoutHistoryError,
  type PlayoutBlock,
  type PlayoutHistoryDetails,
  type PlayoutHistoryEntry,
  type PlayoutSummary
} from '../api';

const PAGE_SIZE_OPTIONS = ['10', '25', '50', '100'];

// Mirrors Blazor's TroubleshootingBlockPlayoutHistoryPageSize ConfigElement — persisted
// client-side here since this screen has no server-side config surface. Namespaced with the
// same `ctv-` prefix as other SPA-persisted preferences (e.g. `ctv-theme`).
const PAGE_SIZE_STORAGE_KEY = 'ctv-block-history-page-size';

function readStoredPageSize(): number {
  try {
    const raw = window.localStorage.getItem(PAGE_SIZE_STORAGE_KEY);
    if (raw != null && PAGE_SIZE_OPTIONS.includes(raw)) {
      return Number(raw);
    }
  } catch {
    // localStorage unavailable (e.g. private browsing) - fall back to the default.
  }
  return 10;
}

function writeStoredPageSize(pageSize: number): void {
  try {
    window.localStorage.setItem(PAGE_SIZE_STORAGE_KEY, String(pageSize));
  } catch {
    // ignore - persistence is a convenience, not a requirement.
  }
}

type PlayoutsState =
  | { status: 'loading'; playouts: []; error: null }
  | { status: 'success'; playouts: PlayoutSummary[]; error: null }
  | { status: 'error'; playouts: []; error: string };

type BlocksState =
  | { status: 'idle'; blocks: []; error: null }
  | { status: 'loading'; blocks: []; error: null }
  | { status: 'success'; blocks: PlayoutBlock[]; error: null }
  | { status: 'error'; blocks: []; error: string };

type HistoryState =
  | { status: 'loading'; entries: []; totalCount: 0; error: null }
  | { status: 'success'; entries: PlayoutHistoryEntry[]; totalCount: number; error: null }
  | { status: 'error'; entries: []; totalCount: 0; error: string };

type DetailsState =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; details: PlayoutHistoryDetails }
  | { status: 'error'; error: string };

interface SelectedBlock {
  id: number;
  name: string;
}

function formatDateTime(value: string): string {
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
}

export function BlockPlayoutTroubleshootingScreen() {
  const [playoutsState, setPlayoutsState] = useState<PlayoutsState>({
    status: 'loading',
    playouts: [],
    error: null
  });
  const [selectedPlayoutId, setSelectedPlayoutId] = useState<number | null>(null);
  const [blocksState, setBlocksState] = useState<BlocksState>({ status: 'idle', blocks: [], error: null });
  const [blockFilter, setBlockFilter] = useState('');
  const [selectedBlock, setSelectedBlock] = useState<SelectedBlock | null>(null);
  const [pageNum, setPageNum] = useState(0);
  const [pageSize, setPageSize] = useState(() => readStoredPageSize());
  const [historyState, setHistoryState] = useState<HistoryState>({
    status: 'loading',
    entries: [],
    totalCount: 0,
    error: null
  });
  const [selectedHistoryId, setSelectedHistoryId] = useState<number | null>(null);
  const [detailsState, setDetailsState] = useState<DetailsState>({ status: 'idle' });

  const activeRef = useRef(true);
  const blocksSeqRef = useRef(0);
  const historySeqRef = useRef(0);
  const detailsSeqRef = useRef(0);

  useEffect(() => {
    activeRef.current = true;
    return () => {
      activeRef.current = false;
    };
  }, []);

  // Load block playouts once (client-side filter to scheduleKind === 'Block').
  useEffect(() => {
    getPlayouts()
      .then((paged) => {
        if (!activeRef.current) {
          return;
        }
        const blockPlayouts = (paged.page ?? []).filter((playout) => playout.scheduleKind === 'Block');
        setPlayoutsState({ status: 'success', playouts: blockPlayouts, error: null });
      })
      .catch((error: unknown) => {
        if (activeRef.current) {
          setPlayoutsState({
            status: 'error',
            playouts: [],
            error: messageFromPlayoutHistoryError(error, 'Unable to load block playouts')
          });
        }
      });
  }, []);

  // Load blocks whenever the selected playout changes. The loading/idle transition is set in the
  // onSelectPlayout handler (never synchronously in this effect) per the screen's setState rules.
  useEffect(() => {
    if (selectedPlayoutId == null) {
      return;
    }

    const id = ++blocksSeqRef.current;
    getPlayoutBlocks(selectedPlayoutId)
      .then((blocks) => {
        if (activeRef.current && id === blocksSeqRef.current) {
          setBlocksState({ status: 'success', blocks, error: null });
        }
      })
      .catch((error: unknown) => {
        if (activeRef.current && id === blocksSeqRef.current) {
          setBlocksState({
            status: 'error',
            blocks: [],
            error: messageFromPlayoutHistoryError(error, 'Unable to load blocks')
          });
        }
      });
  }, [selectedPlayoutId]);

  // Load history whenever the selected block or paging changes.
  const loadHistory = useCallback(() => {
    if (selectedPlayoutId == null || selectedBlock == null) {
      return;
    }

    const id = ++historySeqRef.current;
    getPlayoutBlockHistory(selectedPlayoutId, selectedBlock.id, { pageNum, pageSize })
      .then((paged) => {
        if (activeRef.current && id === historySeqRef.current) {
          setHistoryState({
            status: 'success',
            entries: paged.page ?? [],
            totalCount: paged.totalCount ?? 0,
            error: null
          });
        }
      })
      .catch((error: unknown) => {
        if (activeRef.current && id === historySeqRef.current) {
          setHistoryState({
            status: 'error',
            entries: [],
            totalCount: 0,
            error: messageFromPlayoutHistoryError(error)
          });
        }
      });
  }, [pageNum, pageSize, selectedBlock, selectedPlayoutId]);

  useEffect(() => {
    if (selectedBlock != null) {
      loadHistory();
    }
  }, [loadHistory, selectedBlock]);

  // Decode a history row whenever one is selected. The loading/idle transition is set in the
  // handlers that change selectedHistoryId (never synchronously in this effect).
  useEffect(() => {
    if (selectedHistoryId == null) {
      return;
    }

    const id = ++detailsSeqRef.current;
    getPlayoutHistoryDetails(selectedHistoryId)
      .then((details) => {
        if (activeRef.current && id === detailsSeqRef.current) {
          setDetailsState({ status: 'success', details });
        }
      })
      .catch((error: unknown) => {
        if (activeRef.current && id === detailsSeqRef.current) {
          setDetailsState({
            status: 'error',
            error: messageFromPlayoutHistoryError(error, 'Unable to decode history row')
          });
        }
      });
  }, [selectedHistoryId]);

  const onSelectPlayout = (value: string) => {
    const id = value === '' ? null : Number(value);
    setSelectedPlayoutId(id);
    setBlocksState(
      id == null
        ? { status: 'idle', blocks: [], error: null }
        : { status: 'loading', blocks: [], error: null }
    );
    setSelectedBlock(null);
    setBlockFilter('');
    setSelectedHistoryId(null);
    setDetailsState({ status: 'idle' });
  };

  const onOpenBlockHistory = (block: PlayoutBlock) => {
    setSelectedBlock({ id: block.id, name: block.name });
    setPageNum(0);
    setSelectedHistoryId(null);
    setDetailsState({ status: 'idle' });
    setHistoryState({ status: 'loading', entries: [], totalCount: 0, error: null });
  };

  const onSelectHistoryRow = (historyId: number) => {
    setSelectedHistoryId(historyId);
    setDetailsState({ status: 'loading' });
  };

  const filteredBlocks = useMemo(() => {
    if (blocksState.status !== 'success') {
      return [];
    }
    const needle = blockFilter.trim().toLowerCase();
    if (needle === '') {
      return blocksState.blocks;
    }
    return blocksState.blocks.filter((block) => block.name.toLowerCase().includes(needle));
  }, [blockFilter, blocksState]);

  const groupedBlocks = useMemo(() => {
    const groups = new Map<string, PlayoutBlock[]>();
    for (const block of filteredBlocks) {
      const key = block.groupName ?? '';
      const existing = groups.get(key);
      if (existing) {
        existing.push(block);
      } else {
        groups.set(key, [block]);
      }
    }
    return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b));
  }, [filteredBlocks]);

  const playoutOptions = useMemo(() => {
    const options = playoutsState.playouts.map((playout) => ({
      value: String(playout.id),
      label: `${playout.channelNumber} - ${playout.channelName}`
    }));
    return [{ value: '', label: 'Select a block playout…' }, ...options];
  }, [playoutsState.playouts]);

  const totalPages =
    historyState.status === 'success' ? Math.max(1, Math.ceil(historyState.totalCount / pageSize)) : 1;

  return (
    <div className="ctv-collections">
      <div className="ctv-channels-actionbar">
        <Select
          onChange={(event) => onSelectPlayout(event.target.value)}
          options={playoutOptions}
          style={{ minWidth: 280 }}
          value={selectedPlayoutId == null ? '' : String(selectedPlayoutId)}
        />
        <span className="ctv-channels-spacer" />
      </div>

      {playoutsState.status === 'error' && (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{playoutsState.error}</span>
        </div>
      )}

      {playoutsState.status === 'loading' ? (
        <div className="ctv-collections-loading" role="status">
          <Spinner size={18} />
          <span>Loading block playouts…</span>
        </div>
      ) : selectedPlayoutId == null ? (
        <Card padded>
          <div className="ctv-collections-empty">Select a block playout to inspect its blocks.</div>
        </Card>
      ) : (
        <>
          <div className="ctv-channels-actionbar">
            <Input
              leadingIcon={<Search aria-hidden="true" size={14} />}
              onChange={(event) => setBlockFilter(event.target.value)}
              placeholder="Search for blocks…"
              value={blockFilter}
            />
          </div>

          {blocksState.status === 'error' && (
            <div className="ctv-channels-error" role="alert">
              <TriangleAlert aria-hidden="true" size={15} />
              <span>{blocksState.error}</span>
            </div>
          )}

          <Card padded={false}>
            {blocksState.status === 'loading' ? (
              <div className="ctv-collections-loading" role="status">
                <Spinner size={18} />
                <span>Loading blocks…</span>
              </div>
            ) : filteredBlocks.length === 0 ? (
              <div className="ctv-collections-empty">
                {blocksState.status === 'success' && blocksState.blocks.length > 0
                  ? 'No blocks match this filter.'
                  : 'This playout schedules no blocks.'}
              </div>
            ) : (
              <div className="ctv-channels-table-frame">
                <div className="ctv-channels-table-scroll">
                  <table aria-label="Blocks" className="ctv-channels-table">
                    <thead>
                      <tr>
                        <th>Block</th>
                        <th style={{ width: 120 }}>Minutes</th>
                        <th style={{ width: 120 }}>History</th>
                      </tr>
                    </thead>
                    <tbody>
                      {groupedBlocks.map(([groupName, blocks]) => (
                        <BlockGroupRows
                          blocks={blocks}
                          groupName={groupName}
                          key={groupName}
                          onOpenHistory={onOpenBlockHistory}
                          selectedBlockId={selectedBlock?.id ?? null}
                        />
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>
            )}
          </Card>

          {selectedBlock != null && (
            <>
              <div className="ctv-channels-actionbar">
                <strong>History — {selectedBlock.name}</strong>
                <span className="ctv-channels-spacer" />
                <Select
                  onChange={(event) => {
                    setPageNum(0);
                    const nextPageSize = Number(event.target.value);
                    setPageSize(nextPageSize);
                    writeStoredPageSize(nextPageSize);
                  }}
                  options={PAGE_SIZE_OPTIONS}
                  style={{ width: 96 }}
                  value={String(pageSize)}
                />
              </div>

              {historyState.status === 'error' && (
                <div className="ctv-channels-error" role="alert">
                  <TriangleAlert aria-hidden="true" size={15} />
                  <span>{historyState.error}</span>
                  <span className="ctv-channels-spacer" />
                  <Button onClick={loadHistory} size="sm" variant="secondary">
                    Retry
                  </Button>
                </div>
              )}

              <Card padded={false}>
                {historyState.status === 'loading' ? (
                  <div className="ctv-collections-loading" role="status">
                    <Spinner size={18} />
                    <span>Loading history…</span>
                  </div>
                ) : historyState.entries.length === 0 ? (
                  <div className="ctv-collections-empty">No history recorded for this block.</div>
                ) : (
                  <div className="ctv-channels-table-frame">
                    <div className="ctv-channels-table-scroll">
                      <table aria-label="Block playout history" className="ctv-channels-table">
                        <thead>
                          <tr>
                            <th>Start</th>
                            <th>Finish</th>
                            <th>Key</th>
                            <th>Details</th>
                          </tr>
                        </thead>
                        <tbody>
                          {historyState.entries.map((entry) => (
                            <tr
                              aria-selected={selectedHistoryId === entry.id}
                              className={selectedHistoryId === entry.id ? 'ctv-channel-row-selected' : undefined}
                              key={entry.id}
                              onClick={() => onSelectHistoryRow(entry.id)}
                              style={{ cursor: 'pointer' }}
                            >
                              <td style={{ whiteSpace: 'nowrap' }}>{formatDateTime(entry.when)}</td>
                              <td style={{ whiteSpace: 'nowrap' }}>{formatDateTime(entry.finish)}</td>
                              <td style={{ fontFamily: 'var(--font-mono)' }}>{entry.key}</td>
                              <td style={{ fontFamily: 'var(--font-mono)' }}>{entry.details}</td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    </div>
                    <div className="ctv-channels-footer">
                      <span>
                        {historyState.totalCount} entr{historyState.totalCount === 1 ? 'y' : 'ies'}
                      </span>
                      <span className="ctv-channels-spacer" />
                      <IconButton
                        disabled={pageNum === 0}
                        onClick={() => setPageNum((current) => Math.max(0, current - 1))}
                        size="sm"
                        title="Previous page"
                      >
                        <ChevronLeft aria-hidden="true" size={15} />
                      </IconButton>
                      <span>
                        Page <code>{pageNum + 1}</code> of <code>{totalPages}</code>
                      </span>
                      <IconButton
                        disabled={pageNum + 1 >= totalPages}
                        onClick={() => setPageNum((current) => current + 1)}
                        size="sm"
                        title="Next page"
                      >
                        <ChevronRight aria-hidden="true" size={15} />
                      </IconButton>
                    </div>
                  </div>
                )}
              </Card>

              <HistoryDetailsPanel state={detailsState} />
            </>
          )}
        </>
      )}
    </div>
  );
}

function BlockGroupRows({
  blocks,
  groupName,
  onOpenHistory,
  selectedBlockId
}: {
  blocks: PlayoutBlock[];
  groupName: string;
  onOpenHistory: (block: PlayoutBlock) => void;
  selectedBlockId: number | null;
}) {
  return (
    <>
      <tr className="ctv-channel-group-row">
        <td colSpan={3}>
          <span>{groupName || 'Ungrouped'}</span>
        </td>
      </tr>
      {blocks.map((block) => (
        <tr
          className={selectedBlockId === block.id ? 'ctv-channel-row-selected' : undefined}
          key={block.id}
        >
          <td>{block.name}</td>
          <td>{block.minutes}</td>
          <td>
            {/* Mirrors Blazor BlockPlayoutTroubleshooting.razor: the History action only exists
                for persisted blocks (Id >= 0) — synthesized/virtual blocks have no history to show. */}
            {block.id >= 0 && (
              <Button
                onClick={() => onOpenHistory(block)}
                size="sm"
                startIcon={<History aria-hidden="true" size={14} />}
                variant="secondary"
              >
                History
              </Button>
            )}
          </td>
        </tr>
      ))}
    </>
  );
}

function HistoryDetailsPanel({ state }: { state: DetailsState }) {
  if (state.status === 'idle') {
    return null;
  }

  return (
    <Card padded subtitle="Decoded from the selected history row" title="History details">
      {state.status === 'loading' ? (
        <div className="ctv-collections-loading" role="status">
          <Spinner size={16} />
          <span>Decoding…</span>
        </div>
      ) : state.status === 'error' ? (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{state.error}</span>
        </div>
      ) : (
        <dl className="ctv-detail-grid">
          <DetailRow label="Playback Order" value={state.details.playbackOrder} />
          <DetailRow label="Collection Type" value={state.details.collectionType} />
          <DetailRow label="Name" value={state.details.name} />
          <DetailRow label="Media Item Type" value={state.details.mediaItemType} />
          <DetailRow label="Media Item Title" value={state.details.mediaItemTitle} />
        </dl>
      )}
    </Card>
  );
}

function DetailRow({ label, value }: { label: string; value: string }) {
  return (
    <>
      <dt>{label}</dt>
      <dd>{value === '' ? '—' : value}</dd>
    </>
  );
}
