'use client';

import { useState, useEffect, useRef, useCallback } 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 Server {
  id: string;
  hostname: string;
  host: string;
  status: 'online' | 'offline' | 'maintenance';
  environment: string;
}

type ServersResponse = Server[];

interface TerminalLine {
  id: number;
  type: 'input' | 'output' | 'system' | 'error';
  content: string;
  timestamp: Date;
}

type ConnectionStatus = 'disconnected' | 'connecting' | 'connected';

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

let lineIdCounter = 0;
function nextLineId(): number {
  return ++lineIdCounter;
}

/** Strip common ANSI escape sequences for simplified rendering. */
function stripAnsi(text: string): string {
  // eslint-disable-next-line no-control-regex
  return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
}

/** Derive a WebSocket URL from the current page origin. */
function getWsBaseUrl(): string {
  if (typeof window === 'undefined') return '';
  const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL;
  if (apiBase) {
    try {
      const url = new URL(apiBase);
      const wsProto = url.protocol === 'https:' ? 'wss:' : 'ws:';
      return `${wsProto}//${url.host}`;
    } catch {
      // Relative path -- fall through to use page origin
    }
  }
  return `${proto}//${window.location.host}`;
}

// ---------------------------------------------------------------------------
// Connection status indicator
// ---------------------------------------------------------------------------

function StatusDot({ status }: { status: ConnectionStatus }) {
  const { t } = useTranslation('terminal');
  const colorMap: Record<ConnectionStatus, string> = {
    connected: 'bg-green-500',
    connecting: 'bg-yellow-500 animate-pulse',
    disconnected: 'bg-red-500',
  };

  const labelKeyMap: Record<ConnectionStatus, string> = {
    connected: 'connection.connected',
    connecting: 'connection.connecting',
    disconnected: 'connection.disconnected',
  };

  return (
    <span className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
      <span className={cn('w-2 h-2 rounded-full inline-block', colorMap[status])} />
      {t(labelKeyMap[status])}
    </span>
  );
}

// ---------------------------------------------------------------------------
// Terminal line component
// ---------------------------------------------------------------------------

function TerminalLineRow({ line }: { line: TerminalLine }) {
  const colorMap: Record<TerminalLine['type'], string> = {
    output: 'text-green-400',
    input: 'text-white',
    system: 'text-blue-400',
    error: 'text-red-400',
  };

  return (
    <div className={cn('whitespace-pre-wrap break-all', colorMap[line.type])}>
      {line.type === 'input' && <span className="text-gray-500 select-none">$ </span>}
      {line.content}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Main page component
// ---------------------------------------------------------------------------

export default function TerminalPage() {
  const { t } = useTranslation('terminal');
  // ---- State ----
  const [selectedServerId, setSelectedServerId] = useState<string>('');
  const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('disconnected');
  const [lines, setLines] = useState<TerminalLine[]>([]);
  const [inputValue, setInputValue] = useState('');
  const [commandHistory, setCommandHistory] = useState<string[]>([]);
  const [historyIndex, setHistoryIndex] = useState(-1);

  // ---- Refs ----
  const wsRef = useRef<WebSocket | null>(null);
  const terminalEndRef = useRef<HTMLDivElement | null>(null);
  const inputRef = useRef<HTMLInputElement | null>(null);

  // ---- Query: fetch servers ----
  const { data: serversData, isLoading: serversLoading } = useQuery({
    queryKey: queryKeys.servers.list(),
    queryFn: () => apiClient<ServersResponse>('/api/v1/inventory/servers'),
  });

  const servers = Array.isArray(serversData) ? serversData : [];

  // ---- Auto-scroll ----
  useEffect(() => {
    terminalEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [lines]);

  // ---- Clean up WebSocket on unmount ----
  useEffect(() => {
    return () => {
      wsRef.current?.close();
    };
  }, []);

  // ---- Helpers: add lines ----
  const addLine = useCallback((type: TerminalLine['type'], content: string) => {
    setLines((prev) => [
      ...prev,
      { id: nextLineId(), type, content, timestamp: new Date() },
    ]);
  }, []);

  // ---- Connect ----
  const connect = useCallback(() => {
    if (!selectedServerId) return;
    if (wsRef.current) {
      wsRef.current.close();
    }

    const token = localStorage.getItem('access_token');
    if (!token) {
      addLine('error', t('messages.noAuthToken'));
      return;
    }

    const selectedServer = servers.find((s) => s.id === selectedServerId);
    const hostname = selectedServer?.hostname ?? selectedServerId;

    setConnectionStatus('connecting');
    addLine('system', t('messages.connecting', { hostname, host: selectedServer?.host ?? '...' }));

    const wsBase = getWsBaseUrl();
    const wsUrl = `${wsBase}/ws/terminal?serverId=${encodeURIComponent(selectedServerId)}&token=${encodeURIComponent(token)}`;
    const ws = new WebSocket(wsUrl);
    wsRef.current = ws;

    ws.onopen = () => {
      setConnectionStatus('connected');
      addLine('system', t('messages.connectedTo', { hostname }));
      inputRef.current?.focus();
    };

    ws.onmessage = (event) => {
      let text: string;
      try {
        const parsed = JSON.parse(event.data);
        if (parsed.type === 'output') {
          text = parsed.data ?? '';
        } else if (parsed.type === 'error') {
          addLine('error', parsed.data ?? parsed.message ?? t('messages.unknownError'));
          return;
        } else {
          text = parsed.data ?? event.data;
        }
      } catch {
        // Raw text message
        text = event.data;
      }
      if (text) {
        addLine('output', stripAnsi(text));
      }
    };

    ws.onerror = () => {
      addLine('error', t('messages.wsError'));
    };

    ws.onclose = (event) => {
      setConnectionStatus('disconnected');
      const reason = event.reason ? ` Reason: ${event.reason}` : '';
      addLine('system', t('messages.disconnectedFrom', { hostname, code: event.code, reason }));
      wsRef.current = null;
    };
  }, [selectedServerId, servers, addLine, t]);

  // ---- Disconnect ----
  const disconnect = useCallback(() => {
    if (wsRef.current) {
      wsRef.current.close();
      wsRef.current = null;
    }
    setConnectionStatus('disconnected');
  }, []);

  // ---- Send command ----
  const sendCommand = useCallback(
    (command: string) => {
      if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
        addLine('error', t('messages.notConnected'));
        return;
      }
      addLine('input', command);
      wsRef.current.send(JSON.stringify({ type: 'input', data: command + '\n' }));

      // Update command history
      setCommandHistory((prev) => {
        const updated = [...prev.filter((c) => c !== command), command];
        if (updated.length > 100) updated.shift();
        return updated;
      });
      setHistoryIndex(-1);
    },
    [addLine, t],
  );

  // ---- Handle input key events ----
  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        const cmd = inputValue.trim();
        if (!cmd) return;
        sendCommand(cmd);
        setInputValue('');
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        if (commandHistory.length === 0) return;
        const newIndex =
          historyIndex === -1
            ? commandHistory.length - 1
            : Math.max(0, historyIndex - 1);
        setHistoryIndex(newIndex);
        setInputValue(commandHistory[newIndex]);
      } else if (e.key === 'ArrowDown') {
        e.preventDefault();
        if (historyIndex === -1) return;
        const newIndex = historyIndex + 1;
        if (newIndex >= commandHistory.length) {
          setHistoryIndex(-1);
          setInputValue('');
        } else {
          setHistoryIndex(newIndex);
          setInputValue(commandHistory[newIndex]);
        }
      } else if (e.key === 'l' && e.ctrlKey) {
        e.preventDefault();
        setLines([]);
      }
    },
    [inputValue, commandHistory, historyIndex, sendCommand],
  );

  // ---- Focus terminal on click ----
  const focusInput = useCallback(() => {
    inputRef.current?.focus();
  }, []);

  // ---- Selected server info ----
  const selectedServer = servers.find((s) => s.id === selectedServerId);

  // ---- Render ----
  return (
    <div>
      {/* Header */}
      <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
        <div>
          <h1 className="text-2xl font-bold">{t('title')}</h1>
          <p className="text-sm text-muted-foreground mt-1">
            {t('subtitle')}
          </p>
        </div>
        <StatusDot status={connectionStatus} />
      </div>

      {/* Controls bar */}
      <div className="flex flex-col sm:flex-row items-start sm:items-center gap-3 mb-4">
        {/* Server selector */}
        <div className="flex items-center gap-2 flex-1 min-w-0">
          <label htmlFor="server-select" className="text-sm font-medium whitespace-nowrap">
            {t('server.label')}
          </label>
          <select
            id="server-select"
            value={selectedServerId}
            onChange={(e) => setSelectedServerId(e.target.value)}
            disabled={connectionStatus === 'connected' || connectionStatus === 'connecting'}
            className={cn(
              'w-full max-w-xs px-3 py-2 rounded-md border border-input bg-background text-sm',
              'disabled:opacity-50 disabled:cursor-not-allowed',
            )}
          >
            <option value="">
              {serversLoading ? t('server.loadingServers') : t('server.selectServer')}
            </option>
            {servers.map((server) => (
              <option key={server.id} value={server.id}>
                {server.hostname} ({server.host}) - {server.environment}
                {server.status !== 'online' ? ` [${server.status}]` : ''}
              </option>
            ))}
          </select>
        </div>

        {/* Connect / Disconnect buttons */}
        <div className="flex items-center gap-2">
          {connectionStatus === 'disconnected' && (
            <button
              onClick={connect}
              disabled={!selectedServerId}
              className={cn(
                'px-4 py-2 text-sm rounded-md font-medium transition-colors',
                'bg-primary text-primary-foreground hover:bg-primary/90',
                'disabled:opacity-50 disabled:cursor-not-allowed',
              )}
            >
              {t('actions.connect')}
            </button>
          )}
          {(connectionStatus === 'connected' || connectionStatus === 'connecting') && (
            <button
              onClick={disconnect}
              className={cn(
                'px-4 py-2 text-sm rounded-md font-medium transition-colors',
                'bg-destructive text-destructive-foreground hover:bg-destructive/90',
              )}
            >
              {t('actions.disconnect')}
            </button>
          )}
          <button
            onClick={() => setLines([])}
            className="px-4 py-2 text-sm rounded-md border border-input hover:bg-accent transition-colors"
            title={t('shortcuts.clearTerminal')}
          >
            {t('actions.clear')}
          </button>
        </div>
      </div>

      {/* Terminal container */}
      <div className="bg-gray-950 border rounded-lg overflow-hidden">
        {/* Terminal header bar */}
        <div className="bg-gray-900 px-4 py-2 flex items-center gap-2 border-b border-gray-800">
          <span className="w-3 h-3 rounded-full bg-red-500/80" />
          <span className="w-3 h-3 rounded-full bg-yellow-500/80" />
          <span className="w-3 h-3 rounded-full bg-green-500/80" />
          <span className="ml-3 text-xs text-gray-400 font-mono select-none">
            {selectedServer
              ? `${selectedServer.hostname} (${selectedServer.host})`
              : t('noServerSelected')}
            {connectionStatus === 'connected' && ` - ${t('connection.connected').toLowerCase()}`}
          </span>
        </div>

        {/* Terminal body */}
        <div
          className="h-[calc(100vh-320px)] overflow-y-auto p-4 font-mono text-sm leading-relaxed cursor-text"
          onClick={focusInput}
        >
          {lines.length === 0 && (
            <div className="text-gray-600 select-none">
              {connectionStatus === 'connected'
                ? t('readyMessage')
                : t('selectServerPrompt')}
            </div>
          )}
          {lines.map((line) => (
            <TerminalLineRow key={line.id} line={line} />
          ))}
          <div ref={terminalEndRef} />
        </div>

        {/* Input line */}
        <div className="flex items-center gap-2 px-4 py-2 bg-gray-900 border-t border-gray-800">
          <span className="text-green-500 font-mono text-sm select-none">$</span>
          <input
            ref={inputRef}
            type="text"
            value={inputValue}
            onChange={(e) => {
              setInputValue(e.target.value);
              setHistoryIndex(-1);
            }}
            onKeyDown={handleKeyDown}
            disabled={connectionStatus !== 'connected'}
            placeholder={
              connectionStatus === 'connected'
                ? t('placeholder')
                : t('placeholderDisconnected')
            }
            className={cn(
              'bg-transparent text-green-400 font-mono text-sm flex-1 outline-none',
              'placeholder:text-gray-600 disabled:opacity-50 disabled:cursor-not-allowed',
            )}
            autoComplete="off"
            spellCheck={false}
          />
        </div>
      </div>

      {/* Keyboard shortcuts hint */}
      <div className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
        <span>
          <kbd className="px-1.5 py-0.5 rounded bg-muted text-[10px] font-mono">Enter</kbd> {t('shortcuts.sendCommand')}
        </span>
        <span>
          <kbd className="px-1.5 py-0.5 rounded bg-muted text-[10px] font-mono">Up/Down</kbd> {t('shortcuts.commandHistory')}
        </span>
        <span>
          <kbd className="px-1.5 py-0.5 rounded bg-muted text-[10px] font-mono">Ctrl+L</kbd> {t('shortcuts.clearTerminal')}
        </span>
      </div>
    </div>
  );
}
