"use client";

import { useState, useMemo, useRef, useCallback, useEffect } from "react";
import dynamic from "next/dynamic";
import {
  runSql,
  runDdl,
  ApiError,
  type ColumnMetadata,
  type TableMetadata,
  type RowValues,
} from "@/app/lib/api";
import { useSettings } from "@/app/lib/settings";
import { useTheme } from "@/app/lib/theme";
import { toast } from "sonner";
import { Button } from "./ui/button";
import { Badge } from "./ui/badge";
import { ScrollArea } from "./ui/scroll-area";
import {
  Play,
  Loader2,
  FileCode2,
  AlertCircle,
  CheckCircle2,
  Clock,
  Rows3,
  Eraser,
  History,
  Save,
  ChevronDown,
} from "lucide-react";
import type { editor, languages } from "monaco-editor";
import { cn } from "@/app/lib/utils";

const MonacoEditor = dynamic(
  () => import("@monaco-editor/react").then((m) => m.default),
  {
    ssr: false,
    loading: () => (
      <div className="flex flex-1 items-center justify-center bg-muted">
        <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
      </div>
    ),
  },
);

type Props = {
  clientName: string | null;
  onMutation: () => void;
  tables?: TableMetadata[];
  allColumns?: Map<string, ColumnMetadata[]>;
};

type QueryResult = {
  data: RowValues[] | null;
  error: string | null;
  executionTime: number;
  timestamp: Date;
  query: string;
};

const MAX_HISTORY = 50;

function loadHistory(): { query: string; ts: string }[] {
  try {
    const raw = localStorage.getItem("athena_query_history");
    return raw ? JSON.parse(raw) : [];
  } catch {
    return [];
  }
}

function saveHistory(history: { query: string; ts: string }[]) {
  try {
    localStorage.setItem(
      "athena_query_history",
      JSON.stringify(history.slice(0, MAX_HISTORY)),
    );
  } catch {}
}

export default function SqlEditor({
  clientName,
  onMutation,
  tables,
  allColumns,
}: Props) {
  const { baseUrl, apiKey } = useSettings();
  const { resolved: themeMode } = useTheme();
  const ctx = useMemo(() => ({ baseUrl, apiKey }), [baseUrl, apiKey]);
  const [sql, setSql] = useState("-- Write your SQL here\n\nSELECT 1;\n");
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState<QueryResult | null>(null);
  const [history, setHistory] = useState<{ query: string; ts: string }[]>([]);
  const [showHistory, setShowHistory] = useState(false);
  const [showResults, setShowResults] = useState(true);
  const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
  const monacoRef = useRef<typeof import("monaco-editor") | null>(null);

  useEffect(() => {
    setHistory(loadHistory());
  }, []);

  const handleExecute = useCallback(async () => {
    const query = editorRef.current
      ? (editorRef.current.getModel()?.getValue() ?? sql)
      : sql;
    if (!query.trim() || !clientName) return;
    setLoading(true);
    const start = performance.now();
    try {
      const data = await runSql<RowValues[]>(ctx, query.trim(), { clientName });
      const elapsed = performance.now() - start;
      setResult({
        data: Array.isArray(data) ? data : null,
        error: null,
        executionTime: elapsed,
        timestamp: new Date(),
        query: query.trim(),
      });
      setShowResults(true);
      toast.success("Query executed", {
        description: `${elapsed?.toFixed(0)}ms`,
      });
      onMutation();

      const entry = { query: query.trim(), ts: new Date().toISOString() };
      const updated = [
        entry,
        ...history.filter((h) => h.query !== query.trim()),
      ].slice(0, MAX_HISTORY);
      setHistory(updated);
      saveHistory(updated);
    } catch (err) {
      const elapsed = performance.now() - start;
      const msg = err instanceof ApiError ? err.message : "Execution failed";
      setResult({
        data: null,
        error: msg,
        executionTime: elapsed,
        timestamp: new Date(),
        query: query.trim(),
      });
      toast.error("Query failed", { description: msg });
    } finally {
      setLoading(false);
    }
  }, [sql, clientName, ctx, onMutation, history]);

  const handleEditorMount = useCallback(
    (
      ed: editor.IStandaloneCodeEditor,
      monaco: typeof import("monaco-editor"),
    ) => {
      editorRef.current = ed;
      monacoRef.current = monaco;

      ed.addAction({
        id: "run-query",
        label: "Run Query",
        keybindings: [2048 | 3],
        run: () => {
          handleExecute();
        },
      });

      if (tables && tables.length > 0) {
        const suggestions: languages.CompletionItem[] = [];
        const range = {
          startLineNumber: 1,
          startColumn: 1,
          endLineNumber: 1,
          endColumn: 1,
        };

        for (const t of tables) {
          suggestions.push({
            label: t.table_name,
            kind: monaco.languages.CompletionItemKind.Struct,
            insertText: t.table_name,
            detail: `Table (${t.table_schema ?? "public"})`,
            range,
          });

          const cols = allColumns?.get(t.table_name);
          if (cols) {
            for (const col of cols) {
              suggestions.push({
                label: `${t.table_name}.${col.column_name}`,
                kind: monaco.languages.CompletionItemKind.Field,
                insertText: col.column_name,
                detail: `${col.data_type ?? "unknown"} (${t.table_name})`,
                range,
              });
              suggestions.push({
                label: col.column_name,
                kind: monaco.languages.CompletionItemKind.Field,
                insertText: col.column_name,
                detail: `${col.data_type ?? "unknown"}`,
                range,
              });
            }
          }
        }

        monaco.languages.registerCompletionItemProvider("sql", {
          provideCompletionItems: (model, position) => {
            const word = model.getWordUntilPosition(position);
            const range = {
              startLineNumber: position.lineNumber,
              startColumn: word.startColumn,
              endLineNumber: position.lineNumber,
              endColumn: word.endColumn,
            };
            return {
              suggestions: suggestions.map((s) => ({ ...s, range })),
            };
          },
        });
      }

      ed.focus();
    },
    [handleExecute, tables, allColumns],
  );

  const loadHistoryQuery = useCallback((query: string) => {
    setSql(query);
    editorRef.current?.setValue(query);
    setShowHistory(false);
  }, []);

  const resultColumns = useMemo(() => {
    if (!result?.data || result.data.length === 0) return [];
    return Object.keys(result.data[0]);
  }, [result]);

  return (
    <div className="flex flex-1 flex-col min-h-0">
      {/* Header */}
      <div className="flex items-center gap-3 border-b border-border px-4 py-2.5">
        <FileCode2 className="h-4 w-4 text-primary" />
        <h2 className="text-sm font-semibold text-foreground">SQL Editor</h2>
        <Badge variant="secondary" className="text-caption">
          {clientName ?? "no client"}
        </Badge>
        <div className="flex-1" />
        {result && (
          <span className="flex items-center gap-1 text-caption text-muted-foreground">
            <Clock className="h-3 w-3" />
            {result?.executionTime?.toFixed(0)}ms
          </span>
        )}
        <Button
          variant={showHistory ? "secondary" : "outline"}
          size="sm"
          onClick={() => setShowHistory(!showHistory)}
        >
          <History className="h-3 w-3" />
          History
          {history.length > 0 && (
            <Badge variant="secondary" className="ml-1 text-tiny">
              {history.length}
            </Badge>
          )}
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => {
            setSql("");
            editorRef.current?.setValue("");
            setResult(null);
          }}
        >
          <Eraser className="h-3 w-3" /> Clear
        </Button>
        <Button
          size="sm"
          onClick={handleExecute}
          disabled={loading || !sql.trim() || !clientName}
        >
          {loading ? (
            <Loader2 className="h-3 w-3 animate-spin" />
          ) : (
            <Play className="h-3 w-3" />
          )}
          Run
        </Button>
        <span className="text-caption text-muted-foreground hidden sm:inline">
          ⌘+Enter
        </span>
      </div>

      <div className="flex flex-1 min-h-0">
        {/* History panel */}
        {showHistory && (
          <div className="w-[240px] border-r border-border flex flex-col">
            <div className="px-3 py-2 border-b border-border">
              <span className="text-caption uppercase tracking-wider text-muted-foreground">
                Query History
              </span>
            </div>
            <ScrollArea className="flex-1">
              <div className="p-1.5 space-y-0.5">
                {history.length === 0 && (
                  <p className="text-caption text-muted-foreground text-center py-4">
                    No queries yet
                  </p>
                )}
                {history.map((h, i) => (
                  <button
                    key={`${h.ts}-${i}`}
                    onClick={() => loadHistoryQuery(h.query)}
                    className="w-full text-left rounded px-2 py-1.5 hover:bg-accent-panel transition-colors"
                  >
                    <p className="text-caption text-muted-foreground truncate">
                      {new Date(h.ts).toLocaleTimeString()}
                    </p>
                    <p className="text-xs font-mono text-foreground truncate mt-0.5">
                      {h.query}
                    </p>
                  </button>
                ))}
              </div>
            </ScrollArea>
          </div>
        )}

        {/* Editor + results */}
        <div className="flex flex-1 flex-col min-h-0 min-w-0">
          <div className="flex-1 min-h-[200px]">
            <MonacoEditor
              height="100%"
              language="sql"
              theme={themeMode === "dark" ? "vs-dark" : "light"}
              value={sql}
              onChange={(v) => setSql(v ?? "")}
              onMount={handleEditorMount}
              options={{
                minimap: { enabled: false },
                fontSize: 13,
                lineNumbers: "on",
                scrollBeyondLastLine: false,
                wordWrap: "on",
                padding: { top: 12, bottom: 12 },
                renderLineHighlight: "gutter",
                automaticLayout: true,
                tabSize: 2,
                suggestOnTriggerCharacters: true,
                quickSuggestions: true,
                scrollbar: {
                  verticalScrollbarSize: 6,
                  horizontalScrollbarSize: 6,
                },
                overviewRulerBorder: false,
                hideCursorInOverviewRuler: true,
                contextmenu: true,
                fontFamily:
                  "var(--font-geist-mono), 'Fira Code', 'Cascadia Code', monospace",
                fontLigatures: true,
              }}
            />
          </div>

          {/* Results */}
          <div className="border-t border-border flex flex-col min-h-[60px] max-h-[50%]">
            {!result && (
              <div className="flex flex-col items-center justify-center py-10 text-muted-foreground">
                <div className="flex h-10 w-10 items-center justify-center rounded bg-muted/50 mb-2">
                  <Rows3 className="h-5 w-5 opacity-40" />
                </div>
                <p className="text-xs font-medium">
                  Run a query to see results
                </p>
                <p className="text-body-sm mt-0.5">Press ⌘+Enter to execute</p>
              </div>
            )}
            {result?.error && (
              <div className="flex items-start gap-2 px-4 py-3 bg-destructive-subtle">
                <AlertCircle className="h-4 w-4 text-destructive mt-0.5 shrink-0" />
                <div className="min-w-0">
                  <p className="text-xs font-medium text-destructive">Error</p>
                  <p className="text-xs text-destructive mt-0.5 font-mono break-all">
                    {result.error}
                  </p>
                </div>
              </div>
            )}
            {result?.data && (
              <>
                <div className="flex items-center gap-2 px-4 py-1.5 border-b border-border bg-grid-header">
                  <CheckCircle2 className="h-3.5 w-3.5 text-success" />
                  <span className="text-caption text-foreground font-medium">
                    {result.data.length} rows returned
                  </span>
                  <span className="text-caption text-muted-foreground">
                    · {result?.executionTime?.toFixed(0)}ms
                  </span>
                </div>
                <div className="flex-1 overflow-auto">
                  <table className="w-max min-w-full border-collapse text-xs">
                    <thead className="sticky top-0 z-5">
                      <tr>
                        {resultColumns.map((col) => (
                          <th
                            key={col}
                            className="border-b border-r border-border-muted bg-grid-header px-3 py-1.5 text-left text-caption font-medium text-muted-foreground whitespace-nowrap"
                          >
                            {col}
                          </th>
                        ))}
                      </tr>
                    </thead>
                    <tbody>
                      {result.data.map((row, i) => (
                        <tr
                          key={i}
                          className="border-b border-border-muted hover:bg-accent/30 transition-colors"
                        >
                          {resultColumns.map((col) => {
                            const val = row[col];
                            return (
                              <td
                                key={col}
                                className="border-r border-border-faint px-3 py-1.5 font-mono text-xs whitespace-nowrap max-w-[300px] truncate"
                              >
                                {val === null ? (
                                  <span className="text-muted-foreground italic">
                                    NULL
                                  </span>
                                ) : typeof val === "object" ? (
                                  JSON.stringify(val)
                                ) : (
                                  String(val)
                                )}
                              </td>
                            );
                          })}
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}
