import { useRef, useEffect } from 'react';
import Editor, { Monaco, loader } from '@monaco-editor/react';
import { schemaApi, TableInfo, SchemaData } from '@/services/api';
import * as monacoEditor from 'monaco-editor';

// Configure loader to use local Monaco
loader.config({ monaco: monacoEditor });

interface SqlEditorProps {
  value: string;
  onChange: (value: string) => void;
  databaseId?: string;
  height?: string;
  tabId?: string;
}

// Cache for schema with 5-minute expiration
interface SchemaCache {
  schema: SchemaData;
  timestamp: number;
}

const schemaCache = new Map<string, SchemaCache>();
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes

const getCachedSchema = async (databaseId: string): Promise<SchemaData | null> => {
  const cached = schemaCache.get(databaseId);
  const now = Date.now();

  if (cached && (now - cached.timestamp) < CACHE_DURATION) {
    return cached.schema;
  }

  try {
    const { data } = await schemaApi.getSchema(databaseId);
    if (data.success && data.data) {
      schemaCache.set(databaseId, {
        schema: data.data,
        timestamp: now,
      });
      return data.data;
    }
    return null;
  } catch (error) {
    console.error('Failed to fetch schema:', error);
    return cached?.schema || null;
  }
};

// Global state for completion provider
let completionProviderRegistered = false;
let currentSchema: SchemaData | null = null;

// Parse SQL to find table aliases (e.g., FROM users u, JOIN orders o)
function parseTableAliases(sql: string): Map<string, string> {
  const aliases = new Map<string, string>();

  // Match patterns like: FROM table_name alias, FROM table_name AS alias
  // JOIN table_name alias, JOIN table_name AS alias
  const patterns = [
    /(?:FROM|JOIN)\s+(\w+)(?:\s+(?:AS\s+)?(\w+))?/gi,
    /,\s*(\w+)(?:\s+(?:AS\s+)?(\w+))?/gi,
  ];

  for (const pattern of patterns) {
    let match;
    while ((match = pattern.exec(sql)) !== null) {
      const tableName = match[1].toLowerCase();
      const alias = match[2]?.toLowerCase();
      if (alias && alias !== 'on' && alias !== 'where' && alias !== 'join' && alias !== 'left' && alias !== 'inner' && alias !== 'right') {
        aliases.set(alias, tableName);
      }
      // Also map table name to itself for direct references
      aliases.set(tableName, tableName);
    }
  }

  return aliases;
}

// Find tables mentioned in the query
function findTablesInQuery(sql: string, schema: SchemaData): TableInfo[] {
  const tables: TableInfo[] = [];
  const sqlLower = sql.toLowerCase();

  for (const table of schema.tables) {
    if (sqlLower.includes(table.name.toLowerCase())) {
      tables.push(table);
    }
  }

  return tables;
}

// Get FK suggestions for ON clause
function getFkSuggestions(
  tablesInQuery: TableInfo[],
  schema: SchemaData,
  monaco: Monaco,
  range: any,
  sql: string
): any[] {
  const suggestions: any[] = [];
  const aliases = parseTableAliases(sql);

  // Create reverse alias map: tableName -> alias (or tableName if no alias)
  const tableToAlias = new Map<string, string>();
  for (const [alias, tableName] of aliases) {
    // Prefer shorter alias over table name
    const existing = tableToAlias.get(tableName);
    if (!existing || alias.length < existing.length) {
      tableToAlias.set(tableName, alias);
    }
  }

  for (const table of tablesInQuery) {
    for (const fk of table.foreign_keys) {
      // Find the referenced table
      const refTable = schema.tables.find(t => t.name === fk.references_table);
      if (refTable && tablesInQuery.some(t => t.name === refTable.name)) {
        // Use alias if available, otherwise table name
        const sourceAlias = tableToAlias.get(table.name) || table.name;
        const targetAlias = tableToAlias.get(refTable.name) || refTable.name;

        const joinCondition = `${sourceAlias}.${fk.column} = ${targetAlias}.${fk.references_column}`;
        suggestions.push({
          label: joinCondition,
          kind: monaco.languages.CompletionItemKind.Snippet,
          insertText: joinCondition,
          detail: `FK: ${table.name}.${fk.column} → ${refTable.name}.${fk.references_column}`,
          documentation: `Foreign key relationship between ${table.name} and ${refTable.name}`,
          range,
          sortText: '0' + joinCondition,
        });
      }
    }
  }

  return suggestions;
}

export default function SqlEditor({ value, onChange, databaseId, height, tabId }: SqlEditorProps) {
  const editorRef = useRef<any>(null);
  const monacoRef = useRef<Monaco | null>(null);

  // Fetch schema when database changes
  useEffect(() => {
    if (databaseId) {
      getCachedSchema(databaseId).then(schema => {
        currentSchema = schema;
      });
    } else {
      currentSchema = null;
    }
  }, [databaseId]);

  const handleEditorDidMount = (editor: any, monaco: Monaco) => {
    editorRef.current = editor;
    monacoRef.current = monaco;

    if (completionProviderRegistered) {
      return;
    }
    completionProviderRegistered = true;

    monaco.languages.registerCompletionItemProvider('sql', {
      triggerCharacters: ['.', ' '],
      provideCompletionItems: async (model, position) => {
        const word = model.getWordUntilPosition(position);
        const range = {
          startLineNumber: position.lineNumber,
          endLineNumber: position.lineNumber,
          startColumn: word.startColumn,
          endColumn: word.endColumn,
        };

        const textUntilPosition = model.getValueInRange({
          startLineNumber: 1,
          startColumn: 1,
          endLineNumber: position.lineNumber,
          endColumn: position.column,
        });

        const lineContent = model.getLineContent(position.lineNumber);
        const textBeforeCursor = lineContent.substring(0, position.column - 1);

        let suggestions: any[] = [];

        // Check if we're after a dot (table.column completion)
        const dotMatch = textBeforeCursor.match(/(\w+)\.\s*$/);
        if (dotMatch && currentSchema) {
          const prefix = dotMatch[1].toLowerCase();
          const aliases = parseTableAliases(textUntilPosition);
          const tableName = aliases.get(prefix) || prefix;

          const table = currentSchema.tables.find(t => t.name.toLowerCase() === tableName);
          if (table) {
            // Return only columns for this table
            return {
              suggestions: table.columns.map(col => ({
                label: col.name,
                kind: monaco.languages.CompletionItemKind.Field,
                insertText: col.name,
                detail: `${col.type}${col.is_primary ? ' (PK)' : ''}${!col.nullable ? ' NOT NULL' : ''}`,
                documentation: col.comment || undefined,
                range,
                sortText: col.is_primary ? '0' + col.name : '1' + col.name,
              })),
            };
          }
        }

        // Check if we're after ON keyword (FK suggestions)
        // Match: "ON ", "ON  ", "on ", after JOIN ... ON
        const fullText = model.getValue();
        const onMatch = textBeforeCursor.match(/\bON\s*$/i) || textUntilPosition.match(/\bJOIN\s+\w+\s+\w*\s+ON\s*$/i);
        if (onMatch && currentSchema) {
          const tablesInQuery = findTablesInQuery(fullText, currentSchema);
          const fkSuggestions = getFkSuggestions(tablesInQuery, currentSchema, monaco, range, fullText);
          // Add FK suggestions to the top, but also include other suggestions
          if (fkSuggestions.length > 0) {
            suggestions = [...fkSuggestions, ...suggestions];
            return { suggestions };
          }
        }

        // SQL Keywords
        const keywords = [
          'SELECT', 'FROM', 'WHERE', 'JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN',
          'FULL OUTER JOIN', 'CROSS JOIN', 'ON', 'AND', 'OR', 'NOT', 'IN', 'LIKE',
          'BETWEEN', 'IS NULL', 'IS NOT NULL', 'GROUP BY', 'ORDER BY', 'HAVING',
          'LIMIT', 'OFFSET', 'UNION', 'UNION ALL', 'EXCEPT', 'INTERSECT',
          'INSERT INTO', 'VALUES', 'UPDATE', 'SET', 'DELETE FROM',
          'CREATE TABLE', 'ALTER TABLE', 'DROP TABLE', 'TRUNCATE',
          'DISTINCT', 'AS', 'ASC', 'DESC', 'NULLS FIRST', 'NULLS LAST',
          'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'COALESCE', 'NULLIF',
          'EXISTS', 'NOT EXISTS', 'ANY', 'ALL',
        ];

        suggestions = keywords.map(kw => ({
          label: kw,
          kind: monaco.languages.CompletionItemKind.Keyword,
          insertText: kw + ' ',
          range,
          sortText: '2' + kw,
        }));

        // SQL Functions
        const functions = [
          { name: 'COUNT', snippet: 'COUNT($0)' },
          { name: 'SUM', snippet: 'SUM($0)' },
          { name: 'AVG', snippet: 'AVG($0)' },
          { name: 'MAX', snippet: 'MAX($0)' },
          { name: 'MIN', snippet: 'MIN($0)' },
          { name: 'COALESCE', snippet: 'COALESCE($1, $0)' },
          { name: 'NULLIF', snippet: 'NULLIF($1, $0)' },
          { name: 'CAST', snippet: 'CAST($1 AS $0)' },
          { name: 'CONCAT', snippet: 'CONCAT($0)' },
          { name: 'SUBSTRING', snippet: 'SUBSTRING($1 FROM $2 FOR $0)' },
          { name: 'TRIM', snippet: 'TRIM($0)' },
          { name: 'UPPER', snippet: 'UPPER($0)' },
          { name: 'LOWER', snippet: 'LOWER($0)' },
          { name: 'LENGTH', snippet: 'LENGTH($0)' },
          { name: 'NOW', snippet: 'NOW()' },
          { name: 'CURRENT_DATE', snippet: 'CURRENT_DATE' },
          { name: 'CURRENT_TIMESTAMP', snippet: 'CURRENT_TIMESTAMP' },
          { name: 'DATE_TRUNC', snippet: "DATE_TRUNC('$1', $0)" },
          { name: 'EXTRACT', snippet: 'EXTRACT($1 FROM $0)' },
          { name: 'TO_CHAR', snippet: "TO_CHAR($1, '$0')" },
          { name: 'TO_DATE', snippet: "TO_DATE($1, '$0')" },
          { name: 'ARRAY_AGG', snippet: 'ARRAY_AGG($0)' },
          { name: 'STRING_AGG', snippet: "STRING_AGG($1, '$0')" },
          { name: 'JSON_AGG', snippet: 'JSON_AGG($0)' },
          { name: 'ROW_NUMBER', snippet: 'ROW_NUMBER() OVER ($0)' },
          { name: 'RANK', snippet: 'RANK() OVER ($0)' },
          { name: 'DENSE_RANK', snippet: 'DENSE_RANK() OVER ($0)' },
          { name: 'LAG', snippet: 'LAG($1) OVER ($0)' },
          { name: 'LEAD', snippet: 'LEAD($1) OVER ($0)' },
        ];

        suggestions.push(...functions.map(fn => ({
          label: fn.name,
          kind: monaco.languages.CompletionItemKind.Function,
          insertText: fn.snippet,
          insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
          detail: 'Function',
          range,
          sortText: '3' + fn.name,
        })));

        // Add tables and columns from schema
        if (currentSchema) {
          // Tables
          for (const table of currentSchema.tables) {
            suggestions.push({
              label: table.name,
              kind: monaco.languages.CompletionItemKind.Class,
              insertText: table.name,
              detail: `Table (${table.columns.length} columns)`,
              documentation: table.comment || undefined,
              range,
              sortText: '1' + table.name,
            });

            // All columns (with table prefix for context)
            for (const col of table.columns) {
              suggestions.push({
                label: `${table.name}.${col.name}`,
                kind: monaco.languages.CompletionItemKind.Field,
                insertText: `${table.name}.${col.name}`,
                detail: col.type,
                documentation: col.comment || undefined,
                range,
                sortText: '4' + table.name + col.name,
              });
            }
          }
        }

        return { suggestions };
      },
    });
  };

  return (
    <div className="border border-gray-300 rounded-lg overflow-hidden" style={{ height: height || '100%' }}>
      <Editor
        height="100%"
        language="sql"
        path={tabId}
        value={value}
        onChange={(value) => onChange(value || '')}
        onMount={handleEditorDidMount}
        theme="vs-light"
        options={{
          minimap: { enabled: false },
          fontSize: 14,
          lineNumbers: 'on',
          scrollBeyondLastLine: false,
          automaticLayout: true,
          tabSize: 2,
          wordWrap: 'on',
          formatOnPaste: true,
          formatOnType: true,
          suggestOnTriggerCharacters: true,
          quickSuggestions: true,
          acceptSuggestionOnEnter: 'on',
        }}
      />
    </div>
  );
}
