'use client';

import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { apiClient } from '@/infrastructure/api/api-client';
import { queryKeys } from '@/infrastructure/api/query-keys';
import { cn } from '@/lib/utils';

// -- Types -------------------------------------------------------------------

interface AgentSession {
  id: string;
  taskDescription: string;
  status: 'running' | 'completed' | 'failed' | 'cancelled';
  startedAt: string;
  endedAt?: string;
  durationMs?: number;
  commandCount: number;
  serverTargets: string[];
}

interface SessionEvent {
  id: string;
  sessionId: string;
  type:
    | 'command_executed'
    | 'output_received'
    | 'approval_requested'
    | 'approval_granted'
    | 'approval_denied'
    | 'error'
    | 'session_started'
    | 'session_completed';
  timestamp: string;
  data: {
    command?: string;
    output?: string;
    riskLevel?: string;
    error?: string;
    exitCode?: number;
  };
}

type SessionsResponse = AgentSession[];

type SessionEventsResponse = SessionEvent[];

interface Filters {
  dateFrom: string;
  dateTo: string;
  status: string;
  search: string;
}

// -- Constants ---------------------------------------------------------------

const STATUS_OPTIONS = [
  { labelKey: 'replay.statuses.all', value: '' },
  { labelKey: 'replay.statuses.completed', value: 'completed' },
  { labelKey: 'replay.statuses.failed', value: 'failed' },
  { labelKey: 'replay.statuses.cancelled', value: 'cancelled' },
  { labelKey: 'replay.statuses.running', value: 'running' },
];

const STATUS_BADGE_STYLES: Record<string, string> = {
  running: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400',
  completed: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400',
  failed: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400',
  cancelled: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400',
};

const EVENT_TYPE_STYLES: Record<string, string> = {
  command_executed: 'bg-blue-100 text-blue-800',
  output_received: 'bg-gray-100 text-gray-800',
  approval_requested: 'bg-yellow-100 text-yellow-800',
  approval_granted: 'bg-green-100 text-green-800',
  approval_denied: 'bg-red-100 text-red-800',
  error: 'bg-red-100 text-red-800',
  session_started: 'bg-indigo-100 text-indigo-800',
  session_completed: 'bg-green-100 text-green-800',
};

const EVENT_TYPE_LABEL_KEYS: Record<string, string> = {
  command_executed: 'replay.events.types.commandExecuted',
  output_received: 'replay.events.types.outputReceived',
  approval_requested: 'replay.events.types.approvalRequested',
  approval_granted: 'replay.events.types.approvalGranted',
  approval_denied: 'replay.events.types.approvalDenied',
  error: 'replay.events.types.error',
  session_started: 'replay.events.types.sessionStarted',
  session_completed: 'replay.events.types.sessionCompleted',
};

const RISK_LEVEL_STYLES: Record<string, string> = {
  L0: 'bg-green-100 text-green-800',
  L1: 'bg-yellow-100 text-yellow-800',
  L2: 'bg-orange-100 text-orange-800',
  L3: 'bg-red-100 text-red-800',
};

const PLAYBACK_SPEEDS = [0.5, 1, 2, 4];

// -- Helpers -----------------------------------------------------------------

function formatDuration(ms?: number): string {
  if (ms == null) return '--';
  if (ms < 1000) return `${ms}ms`;
  const seconds = Math.floor(ms / 1000);
  if (seconds < 60) return `${seconds}s`;
  const minutes = Math.floor(seconds / 60);
  const remainingSeconds = seconds % 60;
  if (minutes < 60) return `${minutes}m ${remainingSeconds}s`;
  const hours = Math.floor(minutes / 60);
  const remainingMinutes = minutes % 60;
  return `${hours}h ${remainingMinutes}m`;
}

function formatTimestamp(iso: string): string {
  try {
    return new Date(iso).toLocaleString();
  } catch {
    return iso;
  }
}

function formatRelativeTime(eventIso: string, sessionStartIso: string): string {
  try {
    const diff = new Date(eventIso).getTime() - new Date(sessionStartIso).getTime();
    if (diff < 0) return '+0s';
    return `+${formatDuration(diff)}`;
  } catch {
    return eventIso;
  }
}

function truncateId(id: string, maxLen = 12): string {
  if (id.length <= maxLen) return id;
  return id.slice(0, maxLen) + '...';
}

// -- Main Component ----------------------------------------------------------

export default function SessionReplayPage() {
  const { t } = useTranslation('audit');
  const [filters, setFilters] = useState<Filters>({
    dateFrom: '',
    dateTo: '',
    status: '',
    search: '',
  });
  const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null);
  const [expandedOutputs, setExpandedOutputs] = useState<Set<string>>(new Set());

  // Playback state
  const [isPlaying, setIsPlaying] = useState(false);
  const [playbackSpeed, setPlaybackSpeed] = useState(1);
  const [visibleEventIndex, setVisibleEventIndex] = useState<number>(-1);
  const playbackTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const timelineEndRef = useRef<HTMLDivElement>(null);

  // Build query params
  const queryParams = useMemo(() => {
    const params: Record<string, string> = {};
    if (filters.dateFrom) params.dateFrom = filters.dateFrom;
    if (filters.dateTo) params.dateTo = filters.dateTo;
    if (filters.status) params.status = filters.status;
    if (filters.search) params.search = filters.search;
    return params;
  }, [filters]);

  const queryString = useMemo(() => {
    const sp = new URLSearchParams(queryParams);
    return sp.toString();
  }, [queryParams]);

  // Fetch sessions
  const {
    data: sessionsData,
    isLoading: sessionsLoading,
    error: sessionsError,
  } = useQuery({
    queryKey: queryKeys.sessions.list(queryParams),
    queryFn: () =>
      apiClient<SessionsResponse>(
        `/api/v1/agent/sessions${queryString ? `?${queryString}` : ''}`,
      ),
  });

  const sessions = Array.isArray(sessionsData) ? sessionsData : [];
  const total = sessions.length;

  // Fetch session events when a session is selected
  const {
    data: eventsData,
    isLoading: eventsLoading,
    error: eventsError,
  } = useQuery({
    queryKey: queryKeys.sessions.events(selectedSessionId ?? ''),
    queryFn: () =>
      apiClient<SessionEventsResponse>(
        `/api/v1/agent/sessions/${selectedSessionId}/events`,
      ),
    enabled: !!selectedSessionId,
  });

  const events = Array.isArray(eventsData) ? eventsData : [];
  const selectedSession = sessions.find((s) => s.id === selectedSessionId) ?? null;

  // Reset playback when selecting a new session
  useEffect(() => {
    setIsPlaying(false);
    setVisibleEventIndex(-1);
    setExpandedOutputs(new Set());
    if (playbackTimerRef.current) {
      clearTimeout(playbackTimerRef.current);
      playbackTimerRef.current = null;
    }
  }, [selectedSessionId]);

  // Auto-scroll to the latest visible event during playback
  useEffect(() => {
    if (isPlaying && timelineEndRef.current) {
      timelineEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }
  }, [visibleEventIndex, isPlaying]);

  // Playback logic
  useEffect(() => {
    if (!isPlaying || events.length === 0) return;

    const nextIndex = visibleEventIndex + 1;
    if (nextIndex >= events.length) {
      setIsPlaying(false);
      return;
    }

    // Calculate delay based on time difference between events
    let delayMs = 1000;
    if (nextIndex > 0 && nextIndex < events.length) {
      const prevTime = new Date(events[nextIndex - 1].timestamp).getTime();
      const nextTime = new Date(events[nextIndex].timestamp).getTime();
      const realDiff = nextTime - prevTime;
      // Cap at 3 seconds real-time and scale by speed
      delayMs = Math.min(3000, Math.max(200, realDiff)) / playbackSpeed;
    }

    playbackTimerRef.current = setTimeout(() => {
      setVisibleEventIndex(nextIndex);
    }, delayMs);

    return () => {
      if (playbackTimerRef.current) {
        clearTimeout(playbackTimerRef.current);
      }
    };
  }, [isPlaying, visibleEventIndex, events, playbackSpeed]);

  // Handlers
  const updateFilter = useCallback((key: keyof Filters, value: string) => {
    setFilters((prev) => ({ ...prev, [key]: value }));
  }, []);

  const handleSelectSession = useCallback((id: string) => {
    setSelectedSessionId((prev) => (prev === id ? null : id));
  }, []);

  const toggleOutputExpansion = useCallback((eventId: string) => {
    setExpandedOutputs((prev) => {
      const next = new Set(prev);
      if (next.has(eventId)) {
        next.delete(eventId);
      } else {
        next.add(eventId);
      }
      return next;
    });
  }, []);

  const handlePlay = useCallback(() => {
    if (events.length === 0) return;
    // If at the end, restart from beginning
    if (visibleEventIndex >= events.length - 1) {
      setVisibleEventIndex(-1);
    }
    setIsPlaying(true);
  }, [events, visibleEventIndex]);

  const handlePause = useCallback(() => {
    setIsPlaying(false);
  }, []);

  const handleShowAll = useCallback(() => {
    setIsPlaying(false);
    setVisibleEventIndex(events.length - 1);
  }, [events]);

  const handleReset = useCallback(() => {
    setIsPlaying(false);
    setVisibleEventIndex(-1);
  }, []);

  // Determine which events to show
  const visibleEvents =
    visibleEventIndex < 0 && !isPlaying
      ? events
      : events.slice(0, visibleEventIndex + 1);

  // -- Render ----------------------------------------------------------------

  return (
    <div>
      {/* Header */}
      <div className="mb-6">
        <h1 className="text-2xl font-bold">{t('replay.title')}</h1>
        <p className="text-sm text-muted-foreground mt-1">
          {t('replay.subtitle')}
        </p>
      </div>

      {/* Filters */}
      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4 p-4 bg-card border rounded-lg">
        <div>
          <label className="block text-xs text-muted-foreground mb-1">{t('replay.filters.dateFrom')}</label>
          <input
            type="date"
            value={filters.dateFrom}
            onChange={(e) => updateFilter('dateFrom', e.target.value)}
            className="w-full px-2 py-1.5 bg-input border rounded-md text-sm"
          />
        </div>
        <div>
          <label className="block text-xs text-muted-foreground mb-1">{t('replay.filters.dateTo')}</label>
          <input
            type="date"
            value={filters.dateTo}
            onChange={(e) => updateFilter('dateTo', e.target.value)}
            className="w-full px-2 py-1.5 bg-input border rounded-md text-sm"
          />
        </div>
        <div>
          <label className="block text-xs text-muted-foreground mb-1">{t('replay.filters.status')}</label>
          <select
            value={filters.status}
            onChange={(e) => updateFilter('status', e.target.value)}
            className="w-full px-2 py-1.5 bg-input border rounded-md text-sm"
          >
            {STATUS_OPTIONS.map((opt) => (
              <option key={opt.value} value={opt.value}>
                {t(opt.labelKey)}
              </option>
            ))}
          </select>
        </div>
        <div>
          <label className="block text-xs text-muted-foreground mb-1">{t('replay.filters.search')}</label>
          <input
            type="text"
            value={filters.search}
            onChange={(e) => updateFilter('search', e.target.value)}
            placeholder={t('replay.filters.searchPlaceholder')}
            className="w-full px-2 py-1.5 bg-input border rounded-md text-sm"
          />
        </div>
      </div>

      {/* Loading / Error for sessions */}
      {sessionsLoading && (
        <p className="text-muted-foreground py-4">{t('replay.loading')}</p>
      )}
      {sessionsError && (
        <div className="p-4 mb-4 rounded-md border border-destructive/50 bg-destructive/10 text-destructive text-sm">
          {t('replay.loadError')} {(sessionsError as Error).message}
        </div>
      )}

      {/* Sessions Table */}
      {!sessionsLoading && !sessionsError && (
        <div className="overflow-x-auto border rounded-lg mb-6">
          <table className="w-full text-sm">
            <thead>
              <tr className="border-b bg-muted/50 text-left">
                <th className="py-2 px-3 font-medium">{t('replay.table.sessionId')}</th>
                <th className="py-2 px-3 font-medium">{t('replay.table.taskDescription')}</th>
                <th className="py-2 px-3 font-medium">{t('replay.table.status')}</th>
                <th className="py-2 px-3 font-medium">{t('replay.table.duration')}</th>
                <th className="py-2 px-3 font-medium">{t('replay.table.commands')}</th>
                <th className="py-2 px-3 font-medium">{t('replay.table.startedAt')}</th>
              </tr>
            </thead>
            <tbody>
              {sessions.map((session) => (
                <tr
                  key={session.id}
                  onClick={() => handleSelectSession(session.id)}
                  className={cn(
                    'border-b cursor-pointer hover:bg-accent/50 transition-colors',
                    selectedSessionId === session.id && 'bg-accent/30',
                  )}
                >
                  <td className="py-2 px-3 font-mono text-xs" title={session.id}>
                    {truncateId(session.id)}
                  </td>
                  <td
                    className="py-2 px-3 max-w-xs truncate"
                    title={session.taskDescription}
                  >
                    {session.taskDescription}
                  </td>
                  <td className="py-2 px-3">
                    <span
                      className={cn(
                        'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium capitalize',
                        STATUS_BADGE_STYLES[session.status] ??
                          'bg-muted text-muted-foreground',
                      )}
                    >
                      {session.status}
                    </span>
                  </td>
                  <td className="py-2 px-3 text-muted-foreground whitespace-nowrap">
                    {formatDuration(session.durationMs)}
                  </td>
                  <td className="py-2 px-3 text-muted-foreground text-center">
                    {session.commandCount}
                  </td>
                  <td className="py-2 px-3 text-muted-foreground whitespace-nowrap">
                    {formatTimestamp(session.startedAt)}
                  </td>
                </tr>
              ))}
              {sessions.length === 0 && (
                <tr>
                  <td
                    colSpan={6}
                    className="py-8 text-center text-muted-foreground"
                  >
                    {t('replay.empty')}
                  </td>
                </tr>
              )}
            </tbody>
          </table>
          {total > 0 && (
            <div className="px-3 py-2 text-xs text-muted-foreground border-t bg-muted/30">
              {t('replay.showing', { count: sessions.length, total })}
            </div>
          )}
        </div>
      )}

      {/* Replay Panel */}
      {selectedSession && (
        <div className="border rounded-lg bg-card">
          {/* Session Info Header */}
          <div className="p-4 border-b bg-muted/30">
            <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
              <div>
                <div className="flex items-center gap-3 mb-1">
                  <h2 className="text-lg font-semibold">{t('replay.panel.title')}</h2>
                  <span
                    className={cn(
                      'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium capitalize',
                      STATUS_BADGE_STYLES[selectedSession.status] ??
                        'bg-muted text-muted-foreground',
                    )}
                  >
                    {selectedSession.status}
                  </span>
                </div>
                <div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
                  <span>
                    <span className="font-medium">{t('replay.panel.id')}</span>{' '}
                    <span className="font-mono">{selectedSession.id}</span>
                  </span>
                  <span>
                    <span className="font-medium">{t('replay.panel.duration')}</span>{' '}
                    {formatDuration(selectedSession.durationMs)}
                  </span>
                  <span>
                    <span className="font-medium">{t('replay.panel.commands')}</span>{' '}
                    {selectedSession.commandCount}
                  </span>
                  {selectedSession.serverTargets.length > 0 && (
                    <span>
                      <span className="font-medium">{t('replay.panel.servers')}</span>{' '}
                      {selectedSession.serverTargets.join(', ')}
                    </span>
                  )}
                </div>
              </div>
              <button
                onClick={() => setSelectedSessionId(null)}
                className="px-3 py-1.5 text-xs border rounded-md hover:bg-accent transition-colors self-start"
              >
                {t('replay.panel.close')}
              </button>
            </div>

            {/* Task Description */}
            <p className="text-sm mt-2">{selectedSession.taskDescription}</p>
          </div>

          {/* Playback Controls */}
          <div className="flex flex-wrap items-center gap-3 px-4 py-3 border-b bg-muted/10">
            <div className="flex items-center gap-1">
              {!isPlaying ? (
                <button
                  onClick={handlePlay}
                  disabled={events.length === 0}
                  className="px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                >
                  {t('replay.playback.play')}
                </button>
              ) : (
                <button
                  onClick={handlePause}
                  className="px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
                >
                  {t('replay.playback.pause')}
                </button>
              )}
              <button
                onClick={handleReset}
                disabled={events.length === 0}
                className="px-3 py-1.5 text-xs rounded-md border hover:bg-accent disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
              >
                {t('replay.playback.reset')}
              </button>
              <button
                onClick={handleShowAll}
                disabled={events.length === 0}
                className="px-3 py-1.5 text-xs rounded-md border hover:bg-accent disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
              >
                {t('replay.playback.showAll')}
              </button>
            </div>

            <div className="flex items-center gap-2 ml-auto">
              <span className="text-xs text-muted-foreground">{t('replay.playback.speed')}</span>
              <div className="flex gap-1">
                {PLAYBACK_SPEEDS.map((speed) => (
                  <button
                    key={speed}
                    onClick={() => setPlaybackSpeed(speed)}
                    className={cn(
                      'px-2 py-1 text-xs rounded-md border transition-colors',
                      playbackSpeed === speed
                        ? 'bg-primary text-primary-foreground border-primary'
                        : 'hover:bg-accent',
                    )}
                  >
                    {speed}x
                  </button>
                ))}
              </div>
              <span className="text-xs text-muted-foreground ml-2">
                {visibleEventIndex >= 0
                  ? t('replay.playback.eventsProgress', { current: Math.min(visibleEventIndex + 1, events.length), total: events.length })
                  : `${events.length} ${t('replay.playback.events')}`}
              </span>
            </div>
          </div>

          {/* Events Loading / Error */}
          {eventsLoading && (
            <div className="p-4 text-muted-foreground text-sm">
              {t('replay.events.loading')}
            </div>
          )}
          {eventsError && (
            <div className="p-4 text-red-500 text-sm">
              {t('replay.events.loadError')} {(eventsError as Error).message}
            </div>
          )}

          {/* Event Timeline */}
          {!eventsLoading && !eventsError && (
            <div className="p-4 max-h-[600px] overflow-y-auto">
              {visibleEvents.length === 0 && events.length === 0 && (
                <p className="text-center text-muted-foreground py-8">
                  {t('replay.events.empty')}
                </p>
              )}

              {visibleEvents.length === 0 && events.length > 0 && (
                <p className="text-center text-muted-foreground py-8">
                  {t('replay.events.pressPlay')}
                </p>
              )}

              <div className="space-y-3">
                {visibleEvents.map((event) => (
                  <EventCard
                    key={event.id}
                    event={event}
                    sessionStartedAt={selectedSession.startedAt}
                    isOutputExpanded={expandedOutputs.has(event.id)}
                    onToggleOutput={() => toggleOutputExpansion(event.id)}
                  />
                ))}
              </div>
              <div ref={timelineEndRef} />
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// -- Event Card Component ----------------------------------------------------

function EventCard({
  event,
  sessionStartedAt,
  isOutputExpanded,
  onToggleOutput,
}: {
  event: SessionEvent;
  sessionStartedAt: string;
  isOutputExpanded: boolean;
  onToggleOutput: () => void;
}) {
  const { t } = useTranslation('audit');
  const relativeTime = useMemo(
    () => formatRelativeTime(event.timestamp, sessionStartedAt),
    [event.timestamp, sessionStartedAt],
  );

  return (
    <div className="border rounded-lg p-3 transition-colors hover:bg-muted/20">
      {/* Event header */}
      <div className="flex items-center gap-2 mb-2 flex-wrap">
        <span className="text-xs text-muted-foreground font-mono whitespace-nowrap">
          {relativeTime}
        </span>
        <span
          className={cn(
            'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium',
            EVENT_TYPE_STYLES[event.type] ?? 'bg-muted text-muted-foreground',
          )}
        >
          {EVENT_TYPE_LABEL_KEYS[event.type] ? t(EVENT_TYPE_LABEL_KEYS[event.type]) : event.type}
        </span>
        {event.data.riskLevel && (
          <span
            className={cn(
              'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium',
              RISK_LEVEL_STYLES[event.data.riskLevel] ??
                'bg-muted text-muted-foreground',
            )}
          >
            {event.data.riskLevel}
          </span>
        )}
        {event.data.exitCode != null && (
          <span
            className={cn(
              'text-xs font-mono',
              event.data.exitCode === 0 ? 'text-green-600' : 'text-red-500',
            )}
          >
            exit: {event.data.exitCode}
          </span>
        )}
      </div>

      {/* Command display */}
      {event.data.command && (
        <div className="bg-gray-950 text-green-400 font-mono text-xs p-3 rounded-md overflow-x-auto mb-2">
          <code>{event.data.command}</code>
        </div>
      )}

      {/* Output display */}
      {event.data.output && (
        <div>
          <div
            className={cn(
              'bg-gray-900 text-gray-300 font-mono text-xs p-3 rounded-md overflow-x-auto',
              !isOutputExpanded && 'max-h-40 overflow-y-auto',
            )}
          >
            <pre className="whitespace-pre-wrap break-words">
              {isOutputExpanded
                ? event.data.output
                : event.data.output.length > 500
                  ? event.data.output.slice(0, 500) + '...'
                  : event.data.output}
            </pre>
          </div>
          {event.data.output.length > 500 && (
            <button
              onClick={onToggleOutput}
              className="text-xs text-muted-foreground hover:text-foreground mt-1 underline transition-colors"
            >
              {isOutputExpanded ? t('replay.events.collapse') : t('replay.events.expandFullOutput')}
            </button>
          )}
        </div>
      )}

      {/* Error display */}
      {event.data.error && (
        <div className="bg-red-950/50 text-red-400 font-mono text-xs p-3 rounded-md overflow-x-auto">
          <pre className="whitespace-pre-wrap break-words">{event.data.error}</pre>
        </div>
      )}
    </div>
  );
}
