import type { ReactNode } from 'react';
import {
  ArrowRight,
  Check,
  ExternalLink,
  Info,
  Library,
  ListVideo,
  Radio,
  RefreshCw,
  TriangleAlert,
  Tv
} from 'lucide-react';
import {
  Badge,
  Button,
  Card,
  ChannelLogo,
  ProgressBar,
  Spinner,
  Stat,
  StatusDot
} from '../components';
import {
  useDashboardQuery,
  type DashboardChannel,
  type DashboardChannelState,
  type DashboardHealthCheck,
  type DashboardHealthQueryState
} from '../api';
import { navigateToPath } from '../routing';

type HealthStatus = 'error' | 'idle' | 'live' | 'ok' | 'warn';

function summarizeHealth(
  healthState: DashboardHealthQueryState
): { label: string; status: HealthStatus } {
  if (healthState.status === 'loading') {
    return { label: 'Checking', status: 'idle' };
  }

  if (healthState.status === 'error') {
    return { label: 'Health unavailable', status: 'error' };
  }

  const failedCount = healthState.checks.filter((check) => isErrorHealthStatus(check.status)).length;
  const warningCount = healthState.checks.filter((check) => isWarningHealthStatus(check.status)).length;

  if (failedCount > 0) {
    return { label: `${failedCount} failing`, status: 'error' };
  }

  if (warningCount > 0) {
    return { label: `${warningCount} warning${warningCount === 1 ? '' : 's'}`, status: 'warn' };
  }

  return { label: 'Healthy', status: 'ok' };
}

export function DashboardHealthSummary({ healthState }: { healthState: DashboardHealthQueryState }) {
  const summary = summarizeHealth(healthState);
  return <StatusDot status={summary.status} label={summary.label} />;
}

// The backend serializes health check status as exactly 'pass' | 'fail' | 'warn' | 'info'
// (see ErsatzTV.Application/Health/Mapper.cs GetStatus).
function isWarningHealthStatus(status: string): boolean {
  return status.toLowerCase() === 'warn';
}

function isErrorHealthStatus(status: string): boolean {
  return status.toLowerCase() === 'fail';
}

function isInfoHealthStatus(status: string): boolean {
  return status.toLowerCase() === 'info';
}

function healthIconStatus(status: string): HealthStatus {
  if (isErrorHealthStatus(status)) {
    return 'error';
  }

  if (isWarningHealthStatus(status)) {
    return 'warn';
  }

  if (isInfoHealthStatus(status)) {
    return 'idle';
  }

  return 'ok';
}

function healthIcon(status: string): ReactNode {
  const iconStatus = healthIconStatus(status);

  if (iconStatus === 'error') {
    return <TriangleAlert aria-hidden="true" size={15} />;
  }

  if (iconStatus === 'warn') {
    return <TriangleAlert aria-hidden="true" size={15} />;
  }

  if (iconStatus === 'idle') {
    return <Info aria-hidden="true" size={15} />;
  }

  return <Check aria-hidden="true" size={15} />;
}

function progressFromNowPlaying(nowPlaying: NonNullable<DashboardChannelState['nowPlaying']>): number | null {
  const start = new Date(nowPlaying.startUtc).getTime();
  const finish = new Date(nowPlaying.finishUtc).getTime();
  const now = Date.now();

  if (!Number.isFinite(start) || !Number.isFinite(finish) || finish <= start) {
    return null;
  }

  return Math.round(((now - start) / (finish - start)) * 100);
}

function minutesUntil(value: string): number | null {
  const finish = new Date(value).getTime();

  if (!Number.isFinite(finish)) {
    return null;
  }

  return Math.max(0, Math.ceil((finish - Date.now()) / 60000));
}

function OnAirCard({ channel, state }: { channel: DashboardChannel | null; state: DashboardChannelState }) {
  const name = channel?.name ?? 'Unnamed channel';
  const number = state.channelNumber || channel?.number || `${state.channelId}`;
  const nowPlaying = state.nowPlaying;
  const progress = nowPlaying ? progressFromNowPlaying(nowPlaying) : null;
  const remaining = nowPlaying ? minutesUntil(nowPlaying.finishUtc) : null;

  return (
    <div className="ctv-onair-card">
      <div className="ctv-onair-head">
        <ChannelLogo name={name} size={34} />
        <div>
          <code>{number}</code>
          <strong>{name}</strong>
        </div>
        <Badge tone="accent" dot>
          On air
        </Badge>
      </div>
      <p>{nowPlaying?.title ?? 'Now-playing data unavailable'}</p>
      <ProgressBar value={progress} />
      <div className="ctv-onair-meta">
        <span>{progress == null ? 'Progress unavailable' : `${Math.max(0, Math.min(100, progress))}% elapsed`}</span>
        <span>{remaining == null ? 'Finish unavailable' : `${remaining}m to next`}</span>
      </div>
    </div>
  );
}

function DashboardLoadingState() {
  return (
    <Card>
      <div className="ctv-dashboard-state">
        <Spinner size={20} tone="accent" />
        <span>Loading dashboard</span>
      </div>
    </Card>
  );
}

function DashboardErrorState({ error }: { error: string }) {
  return (
    <Card title={<h2>Dashboard unavailable</h2>} subtitle="Live API request failed">
      <div className="ctv-dashboard-error">
        <span>API request failed</span>
        <strong>{error}</strong>
      </div>
    </Card>
  );
}

const APP_ROUTE_LABELS: Record<string, string> = {
  '/app/trash': 'Open Trash',
  '/app/search': 'Open Search',
  '/app/libraries': 'Open Libraries',
  '/app/schedules': 'Open Schedules',
  '/app/ffmpeg-profiles': 'Open FFmpeg Profiles',
  '/app/settings': 'Open Settings'
};

function remediationLabel(remediation: NonNullable<DashboardHealthCheck['remediation']>): string {
  if (remediation.kind === 'ExternalDoc') {
    return 'View docs';
  }
  const path = remediation.target.split('?')[0].replace(/\/+$/, '');
  return APP_ROUTE_LABELS[path] ?? 'Open';
}

// Renders the "what to do about it" action for a health check, driven by the server's
// remediation metadata: an in-app route (client-side navigation) or an external doc (new tab).
function HealthRemediation({ remediation }: { remediation: DashboardHealthCheck['remediation'] }) {
  if (!remediation) {
    return null;
  }

  const label = remediationLabel(remediation);

  if (remediation.kind === 'ExternalDoc') {
    return (
      <a
        className="ctv-button ctv-button-secondary ctv-button-sm ctv-health-action"
        href={remediation.target}
        target="_blank"
        rel="noreferrer"
      >
        <span>{label}</span>
        <ExternalLink aria-hidden="true" size={13} />
      </a>
    );
  }

  return (
    <Button
      endIcon={<ArrowRight aria-hidden="true" size={13} />}
      onClick={() => navigateToPath(remediation.target)}
      size="sm"
      variant="secondary"
    >
      {label}
    </Button>
  );
}

function HealthPanel({ healthState }: { healthState: DashboardHealthQueryState }) {
  return (
    <Card
      title={<h2>System health</h2>}
      subtitle="Backend health checks"
      actions={
        <Button
          onClick={healthState.refresh}
          loading={healthState.status === 'loading'}
          startIcon={<RefreshCw aria-hidden="true" size={14} />}
          variant="secondary"
        >
          Refresh health
        </Button>
      }
      padded={false}
    >
      <div className="ctv-health-panel">
        {healthState.status === 'loading' && (
          <div className="ctv-health-row">
            <span className="ctv-health-icon ctv-health-icon-idle">
              <Spinner size={15} tone="muted" />
            </span>
            <strong>Health checks</strong>
            <span>Loading current health</span>
            <StatusDot status="idle" />
          </div>
        )}
        {healthState.status === 'error' && (
          <div className="ctv-health-row">
            <span className="ctv-health-icon ctv-health-icon-error">
              <TriangleAlert aria-hidden="true" size={15} />
            </span>
            <strong>Health checks</strong>
            <span>{healthState.error}</span>
            <StatusDot status="error" />
          </div>
        )}
        {healthState.status === 'success' && healthState.checks.length === 0 && (
          <div className="ctv-health-row">
            <span className="ctv-health-icon ctv-health-icon-idle">
              <Info aria-hidden="true" size={15} />
            </span>
            <strong>Health checks</strong>
            <span>No health checks returned</span>
            <StatusDot status="idle" />
          </div>
        )}
        {healthState.status === 'success' && healthState.checks.map((check) => {
          const rowStatus = healthIconStatus(check.status);

          return (
            <div className="ctv-health-row" key={check.title}>
              <span className={`ctv-health-icon ctv-health-icon-${rowStatus}`}>{healthIcon(check.status)}</span>
              <strong>{check.title}</strong>
              <span className="ctv-health-detail">
                <span title={check.detail}>{check.detail}</span>
                <HealthRemediation remediation={check.remediation} />
              </span>
              <StatusDot status={rowStatus} />
            </div>
          );
        })}
      </div>
      <div className="ctv-health-summary">
        <DashboardHealthSummary healthState={healthState} />
      </div>
    </Card>
  );
}

export function DashboardScreen({
  healthState
}: {
  healthState: DashboardHealthQueryState;
}) {
  const dashboardQuery = useDashboardQuery();

  if (dashboardQuery.status === 'loading') {
    return <DashboardLoadingState />;
  }

  if (dashboardQuery.status === 'error') {
    return <DashboardErrorState error={dashboardQuery.error} />;
  }

  const { channels, channelStates, mediaSources, playouts } = dashboardQuery.data;
  const channelsById = new Map(channels.map((channel) => [channel.id, channel]));
  const onAirStates = channelStates.filter((state) => state.onAir).slice(0, 4);
  const playoutCount = playouts.totalCount;
  const libraryCount = mediaSources.reduce((count, source) => count + source.libraries.length, 0);

  return (
    <div className="ctv-screen-stack">
      <section className="ctv-stat-row" aria-label="At a glance">
        <Stat label="Channels" value={channels.length} icon={<Tv aria-hidden="true" size={15} />} />
        <Stat label="Active playouts" value={playoutCount} icon={<ListVideo aria-hidden="true" size={15} />} />
        <Stat label="On air" value={onAirStates.length} icon={<Radio aria-hidden="true" size={15} />} />
        <Stat label="Libraries" value={libraryCount} icon={<Library aria-hidden="true" size={15} />} />
      </section>

      <section className="ctv-dashboard-grid">
        <Card
          title={<h2>On air now</h2>}
          subtitle="Current programmes by channel"
          actions={<Badge tone="accent" dot>{onAirStates.length} on air</Badge>}
        >
          <div className="ctv-onair-grid">
            {onAirStates.length > 0 ? (
              onAirStates.map((state) => (
                <OnAirCard channel={channelsById.get(state.channelId) ?? null} state={state} key={state.channelId} />
              ))
            ) : (
              <div className="ctv-dashboard-empty">No on-air channels reported</div>
            )}
          </div>
        </Card>

        <HealthPanel healthState={healthState} />
      </section>
    </div>
  );
}
