import { Fragment, useEffect, useMemo, useRef, useState } from 'react';

import {
  buildFleetTableViewModel,
  type FleetAuthFilter,
  type FleetFilter,
  type FleetInstance,
  type FleetSortColumn,
  type FleetStatus,
  type SortDirection,
} from './fleet-table-view-model.js';

export interface FleetSnapshot {
  readonly instances: readonly FleetInstance[];
  readonly statuses: ReadonlyMap<string, FleetStatus>;
}
export interface FleetDataSource {
  load(): Promise<FleetSnapshot>;
}
export interface FleetPageProps {
  readonly dataSource?: FleetDataSource;
  readonly initialData?: FleetSnapshot;
  readonly refreshSignal?: number;
}

const EMPTY_SNAPSHOT: FleetSnapshot = { instances: [], statuses: new Map() };
const STATUS_LABELS: Readonly<Record<string, string>> = {
  online: 'Online',
  auth: 'Authentication required',
  offline: 'Offline',
  unknown: 'Unknown',
};
const COLUMN_LABELS: Readonly<Record<FleetSortColumn | 'origin', string>> = {
  name: 'Name',
  status: 'Status',
  latency: 'Latency',
  version: 'Version',
  capabilities: 'Capabilities',
  tags: 'Tags',
  freshness: 'Freshness',
  anomalies: 'Anomalies',
  origin: 'Origin',
};
const ALL_COLUMNS = Object.keys(COLUMN_LABELS) as readonly (FleetSortColumn | 'origin')[];

export function canonicalHttpOrigin(value: string): string | null {
  try {
    const url = new URL(value);
    if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;
    url.username = '';
    url.password = '';
    return url.origin;
  } catch {
    return null;
  }
}

export function FleetPage({ dataSource, initialData, refreshSignal = 0 }: FleetPageProps) {
  const [snapshot, setSnapshot] = useState<FleetSnapshot | null>(initialData ?? null);
  const [error, setError] = useState<string | null>(null);
  const [attempt, setAttempt] = useState(0);
  const [query, setQuery] = useState('');
  const [filter, setFilter] = useState<FleetFilter>('all');
  const [auth, setAuth] = useState<FleetAuthFilter>('all');
  const [capability, setCapability] = useState('');
  const [version, setVersion] = useState('');
  const [tag, setTag] = useState('');
  const [page, setPage] = useState(1);
  const [sort, setSort] = useState<{ column: FleetSortColumn; direction: SortDirection }>({
    column: 'name',
    direction: 'asc',
  });
  const [selectedIds, setSelectedIds] = useState<ReadonlySet<string>>(new Set());
  const [shownColumns, setShownColumns] = useState<ReadonlySet<string>>(new Set(ALL_COLUMNS));
  const [columnsOpen, setColumnsOpen] = useState(false);
  const [batchOpen, setBatchOpen] = useState(false);
  const selectAllRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (initialData && refreshSignal === 0) {
      setSnapshot(initialData);
      setError(null);
      return;
    }
    if (!dataSource) {
      setSnapshot(EMPTY_SNAPSHOT);
      return;
    }
    let active = true;
    setSnapshot(null);
    setError(null);
    void dataSource.load().then(
      (data) => {
        if (active) setSnapshot(data);
      },
      (reason: unknown) => {
        if (active)
          setError(reason instanceof Error ? reason.message : 'Unable to load instances.');
      },
    );
    return () => {
      active = false;
    };
  }, [attempt, dataSource, initialData, refreshSignal]);

  const model = useMemo(
    () =>
      buildFleetTableViewModel(snapshot?.instances ?? [], snapshot?.statuses ?? new Map(), {
        query,
        filter,
        auth,
        ...(capability ? { capability } : {}),
        ...(version ? { version } : {}),
        ...(tag ? { tag } : {}),
        sort,
        selectedIds,
        page,
      }),
    [auth, capability, filter, page, query, selectedIds, snapshot, sort, tag, version],
  );

  useEffect(() => {
    if (selectAllRef.current)
      selectAllRef.current.indeterminate = model.visibleSelection.indeterminate;
  }, [model.visibleSelection.indeterminate]);
  useEffect(() => {
    if (page !== model.page) setPage(model.page);
  }, [model.page, page]);

  function resetPage(action: () => void): void {
    action();
    setPage(1);
  }
  function changeSort(column: FleetSortColumn): void {
    setSort((current) => ({
      column,
      direction: current.column === column && current.direction === 'asc' ? 'desc' : 'asc',
    }));
    setPage(1);
  }
  function toggleOne(id: string, checked: boolean): void {
    setSelectedIds((current) => {
      const next = new Set(current);
      if (checked) next.add(id);
      else next.delete(id);
      return next;
    });
  }
  function toggleVisible(checked: boolean): void {
    setSelectedIds((current) => {
      const next = new Set(current);
      for (const row of model.rows) {
        if (checked) next.add(row.id);
        else next.delete(row.id);
      }
      return next;
    });
  }
  function toggleColumn(column: string): void {
    setShownColumns((current) => {
      const next = new Set(current);
      if (next.has(column)) next.delete(column);
      else next.add(column);
      return next;
    });
  }

  const selectFilter = (
    label: string,
    value: string,
    setValue: (value: string) => void,
    choices: readonly string[],
    allLabel: string,
  ) => (
    <label>
      <span>{label}</span>
      <select
        value={value}
        onChange={(event) => resetPage(() => setValue(event.currentTarget.value))}
      >
        <option value="">{allLabel}</option>
        {choices.map((choice) => (
          <option key={choice} value={choice}>
            {choice}
          </option>
        ))}
      </select>
    </label>
  );

  return (
    <section className="fleet-panel" aria-labelledby="fleet-title">
      <div className="fleet-heading">
        <div>
          <h1 id="fleet-title">Fleet</h1>
          <p>Find, compare, and manage SimAdmin instances.</p>
        </div>
        <div className="fleet-actions">
          <span className="selection-summary" aria-live="polite">
            {model.selectedIds.length} selected
          </span>
          <button
            type="button"
            disabled={model.selectedIds.length === 0}
            onClick={() => setBatchOpen((open) => !open)}
          >
            Batch actions
          </button>
        </div>
      </div>
      {batchOpen ? (
        <div className="batch-entry" role="region" aria-label="Batch action entry">
          Choose an action for {model.selectedIds.length} selected{' '}
          {model.selectedIds.length === 1 ? 'instance' : 'instances'}.
        </div>
      ) : null}
      <div className="fleet-toolbar">
        <label>
          <span>Search instances</span>
          <input
            type="search"
            value={query}
            onChange={(event) => resetPage(() => setQuery(event.currentTarget.value))}
            placeholder="Name, ID, tag, origin…"
          />
        </label>
        <label>
          <span>Status</span>
          <select
            value={filter}
            onChange={(event) =>
              resetPage(() => setFilter(event.currentTarget.value as FleetFilter))
            }
          >
            <option value="all">All statuses</option>
            <option value="online">Online</option>
            <option value="auth">Authentication required</option>
            <option value="offline">Offline</option>
            <option value="unknown">Unknown</option>
          </select>
        </label>
        <label>
          <span>Authentication</span>
          <select
            value={auth}
            onChange={(event) =>
              resetPage(() => setAuth(event.currentTarget.value as FleetAuthFilter))
            }
          >
            <option value="all">All authentication</option>
            <option value="authenticated">Authenticated</option>
            <option value="required">Authentication required</option>
            <option value="unknown">Unknown</option>
          </select>
        </label>
        {selectFilter(
          'Capability',
          capability,
          setCapability,
          model.facets.capabilities,
          'All capabilities',
        )}
        {selectFilter('Version', version, setVersion, model.facets.versions, 'All versions')}
        {selectFilter('Tag', tag, setTag, model.facets.tags, 'All tags')}
        <div className="column-picker">
          <button
            type="button"
            aria-expanded={columnsOpen}
            onClick={() => setColumnsOpen((open) => !open)}
          >
            Choose columns
          </button>
          {columnsOpen ? (
            <fieldset>
              <legend>Visible columns</legend>
              {ALL_COLUMNS.map((column) => (
                <label key={column}>
                  <input
                    type="checkbox"
                    aria-label={`Show ${column} column`}
                    checked={shownColumns.has(column)}
                    onChange={() => toggleColumn(column)}
                  />
                  {COLUMN_LABELS[column]}
                </label>
              ))}
            </fieldset>
          ) : null}
        </div>
      </div>

      {!snapshot && !error ? (
        <p role="status" aria-label="Fleet loading status">
          Loading instances…
        </p>
      ) : null}
      {error ? (
        <div className="state-panel state-error" role="alert">
          <p>Could not load instances: {error}</p>
          <button type="button" onClick={() => setAttempt((value) => value + 1)}>
            Retry loading instances
          </button>
        </div>
      ) : null}
      {snapshot && model.emptyReason ? (
        <div className="state-panel">
          {model.emptyReason === 'config' ? <p>No instances are configured.</p> : null}
          {model.emptyReason === 'search' ? (
            <p>No instances match your search and filters.</p>
          ) : null}
          {model.emptyReason === 'filter' ? <p>No instances match the active filters.</p> : null}
        </div>
      ) : null}
      {snapshot && model.rows.length > 0 ? (
        <>
          <div className="table-scroll" tabIndex={0}>
            <table className="dense-table">
              <caption>Fleet instances</caption>
              <thead>
                <tr>
                  <th scope="col" className="select-column">
                    <input
                      ref={selectAllRef}
                      type="checkbox"
                      aria-label="Select all visible instances"
                      checked={model.visibleSelection.checked}
                      onChange={(event) => toggleVisible(event.currentTarget.checked)}
                    />
                  </th>
                  {ALL_COLUMNS.filter((column) => shownColumns.has(column)).map((column) => (
                    <th
                      key={column}
                      scope="col"
                      aria-sort={
                        column !== 'origin' && sort.column === column
                          ? sort.direction === 'asc'
                            ? 'ascending'
                            : 'descending'
                          : undefined
                      }
                    >
                      {column === 'origin' ? (
                        COLUMN_LABELS[column]
                      ) : (
                        <button
                          type="button"
                          onClick={() => changeSort(column)}
                          aria-label={`Sort by ${column}, currently ${sort.column === column ? sort.direction : 'not sorted'}`}
                        >
                          {COLUMN_LABELS[column]}
                        </button>
                      )}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {model.rows.map((row) => {
                  const origin = canonicalHttpOrigin(row.instance.url);
                  const cells: Readonly<Record<string, React.ReactNode>> = {
                    name: (
                      <th scope="row">
                        <a href={`/instances/${encodeURIComponent(row.id)}/overview`}>
                          {row.displayName}
                        </a>
                        <small>{row.id}</small>
                      </th>
                    ),
                    status: (
                      <td>
                        <span className={`status status-${row.statusKind}`}>
                          {STATUS_LABELS[row.statusKind]}
                        </span>
                      </td>
                    ),
                    latency: <td>{row.latencyMs === undefined ? '—' : `${row.latencyMs} ms`}</td>,
                    version: <td>{row.version ?? '—'}</td>,
                    capabilities: <td>{row.capabilities.join(', ') || '—'}</td>,
                    tags: <td>{row.tags.join(', ') || '—'}</td>,
                    freshness: <td>{row.freshness}</td>,
                    anomalies: <td>{row.anomalies.join(', ') || '—'}</td>,
                    origin: (
                      <td>
                        {origin ? (
                          <a
                            href={origin}
                            target="_blank"
                            rel="noopener noreferrer"
                            aria-label={`Open ${row.displayName} origin`}
                          >
                            {origin}
                          </a>
                        ) : (
                          <span>Invalid origin</span>
                        )}
                      </td>
                    ),
                  };
                  return (
                    <tr key={row.id} aria-selected={row.selected}>
                      <td>
                        <input
                          type="checkbox"
                          aria-label={`Select ${row.displayName}`}
                          checked={row.selected}
                          onChange={(event) => toggleOne(row.id, event.currentTarget.checked)}
                        />
                      </td>
                      {ALL_COLUMNS.filter((column) => shownColumns.has(column)).map((column) => (
                        <Fragment key={column}>{cells[column]}</Fragment>
                      ))}
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
          <nav className="pagination" aria-label="Fleet pagination">
            <button
              type="button"
              aria-label="Previous page"
              disabled={model.page === 1}
              onClick={() => setPage((value) => value - 1)}
            >
              Previous
            </button>
            <span>
              Page {model.page} of {model.pageCount}
            </span>
            <button
              type="button"
              aria-label="Next page"
              disabled={model.page === model.pageCount}
              onClick={() => setPage((value) => value + 1)}
            >
              Next
            </button>
          </nav>
        </>
      ) : null}
    </section>
  );
}
