import { useState, useCallback, useEffect } from 'react';
import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { Select } from '@/components/ui/Select';
import { Badge } from '@/components/ui/Badge';
import { Tabs } from '@/components/ui/Tabs';
import { JsonViewer } from '@/components/ui/JsonViewer';
import { Spinner } from '@/components/ui/Spinner';
import { API_ENDPOINTS } from '@/lib/constants';
import { apiFetch, generateCurl } from '@/api/client';
import {
  connect,
  disconnect,
  listTools,
  callTool,
  getMcpConnectionState,
  onConnectionStateChange,
  type McpToolInfo,
  type McpConnectionState,
} from '@/api/mcp-client';

// ---------------------------------------------------------------------------
// Shared types
// ---------------------------------------------------------------------------

interface HistoryEntry {
  id: number;
  mode: 'REST' | 'MCP';
  label: string;
  status: 'success' | 'error';
  time: string;
  duration: number;
  response: unknown;
}

let historyId = 0;

// ---------------------------------------------------------------------------
// Page
// ---------------------------------------------------------------------------

export function ApiTesterPage() {
  const [mode, setMode] = useState<'rest' | 'mcp'>('rest');
  const [response, setResponse] = useState<unknown>(null);
  const [responseStatus, setResponseStatus] = useState<'success' | 'error' | null>(null);
  const [duration, setDuration] = useState<number | null>(null);
  const [history, setHistory] = useState<HistoryEntry[]>([]);

  const addHistory = useCallback((entry: Omit<HistoryEntry, 'id'>) => {
    setHistory((prev) => [{ ...entry, id: historyId++ }, ...prev].slice(0, 30));
  }, []);

  return (
    <div className="space-y-4">
      <h1 className="text-2xl font-bold">API Tester</h1>

      <Tabs
        tabs={[
          { key: 'rest', label: 'REST API' },
          { key: 'mcp', label: 'MCP Tools' },
        ]}
        active={mode}
        onChange={(k) => setMode(k as 'rest' | 'mcp')}
      />

      <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
        <div className="lg:col-span-2 space-y-4">
          {mode === 'rest' ? (
            <RestPanel
              response={response}
              setResponse={setResponse}
              setResponseStatus={setResponseStatus}
              setDuration={setDuration}
              addHistory={addHistory}
            />
          ) : (
            <McpPanel
              response={response}
              setResponse={setResponse}
              setResponseStatus={setResponseStatus}
              setDuration={setDuration}
              addHistory={addHistory}
            />
          )}

          {/* Response */}
          {response !== null && (
            <Card>
              <div className="flex items-center justify-between mb-2">
                <h2 className="text-sm font-semibold text-dark-muted uppercase tracking-wider">Response</h2>
                <div className="flex items-center gap-2">
                  {responseStatus && (
                    <Badge variant={responseStatus === 'success' ? 'success' : 'danger'}>
                      {responseStatus}
                    </Badge>
                  )}
                  {duration !== null && (
                    <span className="text-xs text-dark-muted">{duration}ms</span>
                  )}
                </div>
              </div>
              <JsonViewer data={response} maxHeight="500px" />
            </Card>
          )}
        </div>

        {/* History */}
        <div>
          <Card>
            <div className="flex items-center justify-between mb-3">
              <h2 className="text-sm font-semibold text-dark-muted uppercase tracking-wider">History</h2>
              <Button variant="ghost" size="sm" onClick={() => setHistory([])}>Clear</Button>
            </div>
            {history.length === 0 ? (
              <p className="text-sm text-dark-muted">No requests yet</p>
            ) : (
              <div className="space-y-2 max-h-[600px] overflow-y-auto">
                {history.map((entry) => (
                  <div
                    key={entry.id}
                    className="p-2 rounded-lg border border-dark-border/50 hover:bg-dark-hover cursor-pointer text-xs"
                    onClick={() => {
                      setResponse(entry.response);
                      setResponseStatus(entry.status);
                      setDuration(entry.duration);
                    }}
                  >
                    <div className="flex items-center gap-2">
                      <Badge
                        variant={entry.mode === 'MCP' ? 'info' : 'success'}
                        className="text-[10px]"
                      >
                        {entry.mode}
                      </Badge>
                      <span className="font-mono truncate">{entry.label}</span>
                    </div>
                    <div className="flex items-center gap-2 mt-1 text-dark-muted">
                      <Badge variant={entry.status === 'success' ? 'success' : 'danger'} className="text-[10px]">
                        {entry.status}
                      </Badge>
                      <span>{entry.time}</span>
                      <span>{entry.duration}ms</span>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </Card>
        </div>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// REST Panel
// ---------------------------------------------------------------------------

function RestPanel({
  setResponse,
  setResponseStatus,
  setDuration,
  addHistory,
}: {
  response: unknown;
  setResponse: (v: unknown) => void;
  setResponseStatus: (v: 'success' | 'error' | null) => void;
  setDuration: (v: number | null) => void;
  addHistory: (e: Omit<HistoryEntry, 'id'>) => void;
}) {
  const [selectedKey, setSelectedKey] = useState<string>(API_ENDPOINTS[0]!.key);
  const [bodyText, setBodyText] = useState('');
  const [loading, setLoading] = useState(false);

  const endpoint = API_ENDPOINTS.find((e) => e.key === selectedKey)!;
  const categories = [...new Set(API_ENDPOINTS.map((e) => e.category))];

  const handleEndpointChange = useCallback((key: string) => {
    setSelectedKey(key);
    setResponse(null);
    setResponseStatus(null);
    setDuration(null);
    const ep = API_ENDPOINTS.find((e) => e.key === key);
    if (ep && 'body' in ep && ep.body) {
      setBodyText(JSON.stringify(ep.body, null, 2));
    } else {
      setBodyText('');
    }
  }, [setResponse, setResponseStatus, setDuration]);

  const handleSend = useCallback(async () => {
    setLoading(true);
    setResponse(null);
    const start = Date.now();
    try {
      let body: unknown = undefined;
      if (bodyText.trim() && endpoint.method !== 'GET') {
        body = JSON.parse(bodyText);
      }
      const res = await apiFetch<unknown>(endpoint.path, {
        method: endpoint.method,
        ...(body ? { body: JSON.stringify(body) } : {}),
      });
      const dur = Date.now() - start;
      setResponse(res);
      setResponseStatus('success');
      setDuration(dur);
      addHistory({ mode: 'REST', label: `${endpoint.method} ${endpoint.path}`, status: 'success', time: new Date().toLocaleTimeString(), duration: dur, response: res });
    } catch (err) {
      const dur = Date.now() - start;
      const errData = err instanceof Error ? { error: err.message } : { error: String(err) };
      setResponse(errData);
      setResponseStatus('error');
      setDuration(dur);
      addHistory({ mode: 'REST', label: `${endpoint.method} ${endpoint.path}`, status: 'error', time: new Date().toLocaleTimeString(), duration: dur, response: errData });
    } finally {
      setLoading(false);
    }
  }, [endpoint, bodyText, setResponse, setResponseStatus, setDuration, addHistory]);

  const curl = generateCurl(
    endpoint.method,
    endpoint.path,
    bodyText.trim() && endpoint.method !== 'GET' ? (() => { try { return JSON.parse(bodyText) as unknown; } catch { return undefined; } })() : undefined,
  );

  return (
    <>
      <Card>
        <h2 className="text-sm font-semibold text-dark-muted uppercase tracking-wider mb-3">REST Request</h2>
        <div className="space-y-3">
          <Select
            label="Endpoint"
            options={categories.flatMap((cat) => [
              { value: `__cat_${cat}`, label: `── ${cat} ──` },
              ...API_ENDPOINTS.filter((e) => e.category === cat).map((e) => ({
                value: e.key,
                label: `${e.method} ${e.path}`,
              })),
            ])}
            value={selectedKey}
            onChange={(e) => {
              if (!e.target.value.startsWith('__cat_')) {
                handleEndpointChange(e.target.value);
              }
            }}
          />
          <div className="flex items-center gap-2">
            <Badge variant={endpoint.method === 'GET' ? 'success' : endpoint.method === 'DELETE' ? 'danger' : 'info'}>
              {endpoint.method}
            </Badge>
            <code className="text-sm text-dark-text font-mono">{endpoint.path}</code>
          </div>
          {endpoint.method !== 'GET' && (
            <div className="flex flex-col gap-1.5">
              <label className="text-sm text-dark-muted">Request Body (JSON)</label>
              <textarea
                value={bodyText}
                onChange={(e) => setBodyText(e.target.value)}
                className="bg-dark-bg border border-dark-border rounded-lg px-3 py-2 text-sm text-dark-text font-mono focus:outline-none focus:border-dark-accent transition-colors resize-y min-h-[120px]"
                spellCheck={false}
              />
            </div>
          )}
          <div className="flex gap-2">
            <Button onClick={() => void handleSend()} loading={loading}>Send Request</Button>
            <Button variant="ghost" size="sm" onClick={() => void navigator.clipboard.writeText(curl)}>Copy cURL</Button>
          </div>
        </div>
      </Card>
      <Card>
        <h2 className="text-sm font-semibold text-dark-muted uppercase tracking-wider mb-2">cURL</h2>
        <pre className="bg-dark-bg border border-dark-border rounded-lg p-3 text-xs font-mono text-dark-text overflow-x-auto whitespace-pre-wrap">{curl}</pre>
      </Card>
    </>
  );
}

// ---------------------------------------------------------------------------
// MCP Panel
// ---------------------------------------------------------------------------

function McpPanel({
  setResponse,
  setResponseStatus,
  setDuration,
  addHistory,
}: {
  response: unknown;
  setResponse: (v: unknown) => void;
  setResponseStatus: (v: 'success' | 'error' | null) => void;
  setDuration: (v: number | null) => void;
  addHistory: (e: Omit<HistoryEntry, 'id'>) => void;
}) {
  const [connState, setConnState] = useState<McpConnectionState>(getMcpConnectionState());
  const [connecting, setConnecting] = useState(false);
  const [tools, setTools] = useState<McpToolInfo[]>([]);
  const [selectedTool, setSelectedTool] = useState('');
  const [argsText, setArgsText] = useState('{}');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    onConnectionStateChange(setConnState);
    return () => onConnectionStateChange(() => {});
  }, []);

  const handleConnect = useCallback(async () => {
    setConnecting(true);
    try {
      await connect();
      const result = await listTools();
      setTools(result.tools);
      if (result.tools.length > 0 && !selectedTool) {
        setSelectedTool(result.tools[0]!.name);
        // Generate default args from schema
        const schema = result.tools[0]!.inputSchema;
        setArgsText(generateDefaultArgs(schema));
      }
    } catch (err) {
      setResponse({ error: err instanceof Error ? err.message : String(err) });
      setResponseStatus('error');
    } finally {
      setConnecting(false);
    }
  }, [selectedTool, setResponse, setResponseStatus]);

  const handleDisconnect = useCallback(() => {
    disconnect();
    setTools([]);
    setSelectedTool('');
  }, []);

  const handleToolChange = useCallback((name: string) => {
    setSelectedTool(name);
    setResponse(null);
    setResponseStatus(null);
    setDuration(null);
    const tool = tools.find((t) => t.name === name);
    if (tool) {
      setArgsText(generateDefaultArgs(tool.inputSchema));
    }
  }, [tools, setResponse, setResponseStatus, setDuration]);

  const handleCall = useCallback(async () => {
    if (!selectedTool) return;
    setLoading(true);
    setResponse(null);
    const start = Date.now();
    try {
      let args: Record<string, unknown> = {};
      if (argsText.trim()) {
        args = JSON.parse(argsText) as Record<string, unknown>;
      }
      const res = await callTool(selectedTool, args);
      const dur = Date.now() - start;
      setResponse(res);
      setResponseStatus('success');
      setDuration(dur);
      addHistory({ mode: 'MCP', label: selectedTool, status: 'success', time: new Date().toLocaleTimeString(), duration: dur, response: res });
    } catch (err) {
      const dur = Date.now() - start;
      const errData = err instanceof Error ? { error: err.message } : { error: String(err) };
      setResponse(errData);
      setResponseStatus('error');
      setDuration(dur);
      addHistory({ mode: 'MCP', label: selectedTool, status: 'error', time: new Date().toLocaleTimeString(), duration: dur, response: errData });
    } finally {
      setLoading(false);
    }
  }, [selectedTool, argsText, setResponse, setResponseStatus, setDuration, addHistory]);

  const currentTool = tools.find((t) => t.name === selectedTool);

  return (
    <>
      {/* Connection */}
      <Card>
        <h2 className="text-sm font-semibold text-dark-muted uppercase tracking-wider mb-3">MCP Connection</h2>
        <div className="flex items-center gap-3">
          <Badge variant={connState === 'connected' ? 'success' : connState === 'connecting' ? 'warning' : 'default'}>
            {connState}
          </Badge>
          {connState === 'disconnected' && (
            <Button size="sm" onClick={() => void handleConnect()} loading={connecting}>
              Connect
            </Button>
          )}
          {connState === 'connected' && (
            <>
              <span className="text-xs text-dark-muted">{tools.length} tools available</span>
              <Button size="sm" variant="ghost" onClick={handleDisconnect}>Disconnect</Button>
            </>
          )}
          {connState === 'connecting' && <Spinner size="sm" />}
        </div>
      </Card>

      {/* Tool Call */}
      {connState === 'connected' && tools.length > 0 && (
        <Card>
          <h2 className="text-sm font-semibold text-dark-muted uppercase tracking-wider mb-3">MCP Tool Call</h2>
          <div className="space-y-3">
            <Select
              label="Tool"
              options={tools.map((t) => ({ value: t.name, label: t.name }))}
              value={selectedTool}
              onChange={(e) => handleToolChange(e.target.value)}
            />

            {/* Tool info */}
            {currentTool && (
              <div className="bg-dark-bg rounded-lg p-3 text-xs space-y-2">
                {currentTool.description && (
                  <p className="text-dark-muted">{currentTool.description}</p>
                )}
                {currentTool.inputSchema?.properties && (
                  <div>
                    <p className="text-dark-muted font-semibold mb-1">Parameters:</p>
                    {Object.entries(currentTool.inputSchema.properties).map(([key, prop]) => (
                      <div key={key} className="flex gap-2 ml-2">
                        <code className="text-dark-accent">{key}</code>
                        <span className="text-dark-muted">
                          {prop.type || ''}
                          {currentTool.inputSchema?.required?.includes(key) ? ' (required)' : ' (optional)'}
                          {prop.description ? ` — ${prop.description}` : ''}
                        </span>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            )}

            {/* Arguments editor */}
            <div className="flex flex-col gap-1.5">
              <label className="text-sm text-dark-muted">Arguments (JSON)</label>
              <textarea
                value={argsText}
                onChange={(e) => setArgsText(e.target.value)}
                className="bg-dark-bg border border-dark-border rounded-lg px-3 py-2 text-sm text-dark-text font-mono focus:outline-none focus:border-dark-accent transition-colors resize-y min-h-[120px]"
                spellCheck={false}
              />
            </div>

            <Button onClick={() => void handleCall()} loading={loading}>
              Call Tool
            </Button>
          </div>
        </Card>
      )}

      {connState === 'disconnected' && (
        <Card>
          <div className="text-center py-8 text-dark-muted">
            <p>Connect to the MCP server to discover and test tools.</p>
            <p className="text-xs mt-2">This uses the same SSE + JSON-RPC protocol that AI clients use.</p>
          </div>
        </Card>
      )}
    </>
  );
}

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

function generateDefaultArgs(schema?: McpToolInfo['inputSchema']): string {
  if (!schema?.properties) return '{}';
  const obj: Record<string, unknown> = {};
  for (const [key, prop] of Object.entries(schema.properties)) {
    if (prop.default !== undefined) {
      obj[key] = prop.default;
    } else if (prop.enum && prop.enum.length > 0) {
      obj[key] = prop.enum[0];
    } else if (prop.type === 'string') {
      obj[key] = '';
    } else if (prop.type === 'boolean') {
      obj[key] = false;
    } else if (prop.type === 'number' || prop.type === 'integer') {
      obj[key] = 0;
    } else if (prop.type === 'array') {
      obj[key] = [];
    } else {
      obj[key] = null;
    }
  }
  return JSON.stringify(obj, null, 2);
}
