/**
 * Formula Editor component with validation, syntax highlighting, and intelligent autocomplete.
 */

import React, { useState, useEffect, useCallback, useRef } from "react";
import { Check, X, HelpCircle } from "lucide-react";
import { invoke } from "@tauri-apps/api/core";
import {
  TARGET_FIELD_DISPLAY_NAMES,
  type TargetField,
  type FormulaValidationResult,
} from "../../types/mappings";
import type { AvailableXbrlConcept } from "../../types/financial";
import { FormulaAutocomplete } from "./FormulaAutocomplete";
import {
  detectTrigger,
  filterConcepts,
  filterFields,
  conceptToSuggestion,
  fieldToSuggestion,
  insertCompletion,
  getTextareaPosition,
  type TriggerInfo,
  type SuggestionItem,
} from "../../lib/formulaAutocompleteUtils";

/**
 * Hook to detect clicks outside a component.
 */
function useClickOutside(
  ref: React.RefObject<HTMLDivElement | null>,
  callback: () => void,
) {
  useEffect(() => {
    const handleClick = (event: MouseEvent) => {
      if (ref.current && !ref.current.contains(event.target as Node)) {
        callback();
      }
    };

    document.addEventListener("mousedown", handleClick);
    return () => {
      document.removeEventListener("mousedown", handleClick);
    };
  }, [ref, callback]);
}

interface FormulaEditorProps {
  value: string;
  onChange: (value: string) => void;
  onValidate?: (result: FormulaValidationResult) => void;
  placeholder?: string;
  availableFields?: TargetField[];
  availableConcepts?: AvailableXbrlConcept[];
  disabled?: boolean;
}

export const FormulaEditor: React.FC<FormulaEditorProps> = ({
  value,
  onChange,
  onValidate,
  placeholder = "e.g., ($gross_profit / $total_revenues) * 100",
  availableFields = Object.keys(TARGET_FIELD_DISPLAY_NAMES) as TargetField[],
  availableConcepts = [],
  disabled = false,
}) => {
  const [validation, setValidation] = useState<FormulaValidationResult | null>(
    null,
  );
  const [showHelp, setShowHelp] = useState(false);
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  // Autocomplete state
  const [showAutocomplete, setShowAutocomplete] = useState(false);
  const [autocompleteSuggestions, setAutocompleteSuggestions] = useState<
    SuggestionItem[]
  >([]);
  const [selectedIndex, setSelectedIndex] = useState(0);
  const [triggerInfo, setTriggerInfo] = useState<TriggerInfo | null>(null);
  const [autocompletePosition, setAutocompletePosition] = useState<{ top: number; left: number; width: number } | null>(null);
  const autocompleteRef = useRef<HTMLDivElement>(null);

  // Close autocomplete when clicking outside
  useClickOutside(autocompleteRef, () => {
    if (showAutocomplete) {
      setShowAutocomplete(false);
      setTriggerInfo(null);
    }
  });

  const validateFormula = useCallback(
    (formula: string) => {
      try {
        const clientResult = validateAndTokenize(formula, availableFields);
        if (!clientResult.validation.isValid) {
          setValidation(clientResult.validation);
          onValidate?.(clientResult.validation);
          return clientResult.validation;
        }

        invoke<FormulaValidationResult>("validate_formula", {
          expression: formula,
        })
          .then((backendResult) => {
            const merged: FormulaValidationResult = {
              isValid: backendResult.isValid,
              error: backendResult.error,
              fieldRefs: backendResult.fieldRefs ?? clientResult.validation.fieldRefs,
              functions: backendResult.functions ?? clientResult.validation.functions,
            };
            setValidation(merged);
            onValidate?.(merged);
          })
          .catch((error) => {
            const errorResult: FormulaValidationResult = {
              isValid: false,
              error: String(error),
              fieldRefs: clientResult.validation.fieldRefs,
              functions: clientResult.validation.functions,
            };
            setValidation(errorResult);
            onValidate?.(errorResult);
          });

        setValidation(clientResult.validation);
        onValidate?.(clientResult.validation);
        return clientResult.validation;
      } catch (error) {
        console.error("Validation error:", error);
        const errorResult: FormulaValidationResult = {
          isValid: false,
          error: error instanceof Error ? error.message : "Validation error",
        };
        setValidation(errorResult);
        onValidate?.(errorResult);
        return errorResult;
      }
    },
    [availableFields, onValidate],
  );

  useEffect(() => {
    if (value) {
      validateFormula(value);
    }
  }, [value, validateFormula]);

  // Update suggestions when trigger info or available concepts change
  useEffect(() => {
    if (!triggerInfo) {
      setAutocompleteSuggestions([]);
      setShowAutocomplete(false);
      return;
    }

    let suggestions: SuggestionItem[] = [];

    if (triggerInfo.triggerChar === "#") {
      // XBRL concept suggestions
      const filteredConcepts = filterConcepts(
        availableConcepts,
        triggerInfo.query,
      );
      suggestions = filteredConcepts.map(conceptToSuggestion);
    } else if (triggerInfo.triggerChar === "$") {
      // Field reference suggestions
      const filteredFields = filterFields(availableFields, triggerInfo.query);
      suggestions = filteredFields.map((field) =>
        fieldToSuggestion(field, TARGET_FIELD_DISPLAY_NAMES),
      );
    }

    setAutocompleteSuggestions(suggestions);
    setShowAutocomplete(suggestions.length > 0);
    setSelectedIndex(0);
  }, [triggerInfo, availableConcepts, availableFields]);

  const handleChange = (newValue: string) => {
    onChange(newValue);

    // Update cursor position and check for triggers
    const textarea = textareaRef.current;
    if (textarea) {
      const newCursorPosition = textarea.selectionStart;

      const trigger = detectTrigger(newValue, newCursorPosition);
      setTriggerInfo(trigger);

      // Calculate autocomplete position when trigger is detected
      if (trigger) {
        const position = getTextareaPosition(textarea);
        if (position) {
          setAutocompletePosition({
            ...position,
            width: textarea.offsetWidth
          });
        }
      }

      // Close autocomplete if we moved away from a valid trigger
      if (!trigger && showAutocomplete) {
        setShowAutocomplete(false);
      }
    }

    // Validate formula (but don't block input)
    if (newValue) {
      validateFormula(newValue);
    } else {
      setValidation(null);
    }
  };

  const handleSelectionChange = () => {
    const textarea = textareaRef.current;
    if (textarea) {
      const trigger = detectTrigger(value, textarea.selectionStart);
      setTriggerInfo(trigger);

      // Calculate autocomplete position when trigger is detected
      if (trigger) {
        const position = getTextareaPosition(textarea);
        if (position) {
          setAutocompletePosition({
            ...position,
            width: textarea.offsetWidth
          });
        }
      }

      if (!trigger) {
        setShowAutocomplete(false);
      }
    }
  };

  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (!showAutocomplete || autocompleteSuggestions.length === 0) {
      return;
    }

    switch (e.key) {
      case "ArrowDown":
        e.preventDefault();
        e.stopPropagation();
        setSelectedIndex((prev) =>
          prev < autocompleteSuggestions.length - 1 ? prev + 1 : 0,
        );
        break;

      case "ArrowUp":
        e.preventDefault();
        e.stopPropagation();
        setSelectedIndex((prev) =>
          prev > 0 ? prev - 1 : autocompleteSuggestions.length - 1,
        );
        break;

      case "Tab":
      case "Enter":
        e.preventDefault();
        e.stopPropagation();
        if (autocompleteSuggestions[selectedIndex]) {
          handleSelectSuggestion(selectedIndex);
        }
        break;

      case "Escape":
        e.preventDefault();
        e.stopPropagation();
        setShowAutocomplete(false);
        setTriggerInfo(null);
        break;

      default:
        // Let other keys through
        break;
    }
  };

  const handleSelectSuggestion = useCallback((index: number) => {
    const suggestion = autocompleteSuggestions[index];
    if (!suggestion || !triggerInfo) {
      return;
    }

    const newValue = insertCompletion(
      value,
      triggerInfo.triggerRange,
      suggestion.insertText,
    );

    handleChange(newValue);
    setShowAutocomplete(false);
    setTriggerInfo(null);

    // Focus textarea and move cursor to end of inserted text
    requestAnimationFrame(() => {
      if (textareaRef.current) {
        const newCursorPosition =
          triggerInfo.triggerRange.start + suggestion.insertText.length;
        textareaRef.current.focus();
        textareaRef.current.setSelectionRange(
          newCursorPosition,
          newCursorPosition,
        );
      }
    });
  }, [autocompleteSuggestions, triggerInfo, value, handleChange]);

  const handleHoverSuggestion = useCallback((index: number) => {
    setSelectedIndex(index);
  }, []);

  return (
    <div className="space-y-3" style={{ pointerEvents: "auto" }}>
      <div className="relative" style={{ pointerEvents: "auto" }}>
        <div className="flex items-center gap-2">
          <textarea
            ref={textareaRef}
            value={value}
            onChange={(e) => {
              handleChange(e.target.value);
            }}
            onSelect={handleSelectionChange}
            onKeyDown={handleKeyDown}
            placeholder={placeholder}
            disabled={disabled}
            className="flex-1 min-h-[80px] rounded border border-term-border bg-term-surface px-3 py-2 text-sm font-mono text-term-text outline-none transition-colors focus:border-info disabled:cursor-not-allowed disabled:opacity-50 resize-y"
            spellCheck={false}
            rows={2}
            style={{ pointerEvents: "auto" }}
          />
          {validation && !disabled && (
            <div
              className={validation.isValid ? "text-positive" : "text-negative"}
            >
              {validation.isValid ? (
                <Check className="h-4 w-4" />
              ) : (
                <X className="h-4 w-4" />
              )}
            </div>
          )}
          <button
            type="button"
            onClick={() => setShowHelp(!showHelp)}
            disabled={disabled}
            className="rounded border border-term-border bg-term-highlight p-2 text-term-text-muted transition-colors hover:border-term-border-focus hover:text-term-text disabled:cursor-not-allowed disabled:opacity-50"
            title="Show formula help"
          >
            <HelpCircle className="h-4 w-4" />
          </button>
        </div>

        {showAutocomplete && triggerInfo && autocompletePosition && (
          <div
            ref={autocompleteRef}
            className="absolute z-50"
          >
            <FormulaAutocomplete
              suggestions={autocompleteSuggestions}
              selectedIndex={selectedIndex}
              onSelect={handleSelectSuggestion}
              onHover={handleHoverSuggestion}
              position={autocompletePosition}
              query={triggerInfo.query}
            />
          </div>
        )}

        {validation && !validation.isValid && validation.error && (
          <div className="mt-2 text-xs font-mono text-negative">
            {validation.error}
          </div>
        )}

        {validation?.fieldRefs && validation.fieldRefs.length > 0 && (
          <div className="mt-2 flex flex-wrap gap-1">
            {validation.fieldRefs.map((field) => (
              <span
                key={field}
                className="rounded border border-term-border-subtle bg-term-highlight px-2 py-0.5 text-xs font-mono text-term-text-muted"
              >
                ${field}
              </span>
            ))}
          </div>
        )}
      </div>

      {showHelp && <FormulaHelp availableFields={availableFields} />}
    </div>
  );
};

interface FormulaHelpProps {
  availableFields: TargetField[];
}

const FormulaHelp: React.FC<FormulaHelpProps> = ({ availableFields }) => {
  return (
    <div className="rounded border border-term-border bg-term-bg p-4">
      <div className="mb-2 text-xs font-semibold text-term-text">
        Field References
      </div>
      <p className="mb-3 text-xs text-term-text-muted">
        Use <code className="text-info">$field_name</code> to reference fields (snake_case, type <code className="text-info">$</code> for autocomplete)
      </p>
      <div className="grid max-h-48 grid-cols-2 gap-2 overflow-y-auto mb-4">
        {availableFields.map((field) => (
          <div
            key={field}
            className="rounded border border-term-border-subtle bg-term-surface px-2 py-1 text-xs font-mono text-term-text-muted"
          >
            ${field}
            <span className="ml-2 text-term-text-tertiary">
              {TARGET_FIELD_DISPLAY_NAMES[field]}
            </span>
          </div>
        ))}
      </div>

      <div className="mb-2 text-xs font-semibold text-term-text">
        XBRL Concept References
      </div>
      <p className="mb-3 text-xs text-term-text-muted">
        Use <code className="text-info">#taxonomy.concept[unit_family]</code> to
        reference XBRL concepts
      </p>
      <div className="rounded border border-term-border-subtle bg-term-surface p-3 text-xs font-mono text-term-text-muted">
        <div className="mb-1">#us-gaap.Revenue[currency]</div>
        <div className="mb-1">#ifrs-full.GrossProfit[currency]</div>
        <div>#dei.EntityCommonStockSharesOutstanding[shares]</div>
      </div>

      <div className="mb-3 mt-4 text-xs font-semibold text-term-text">
        Functions
      </div>
      <div className="space-y-1">
        {FUNCTIONS.map((func) => (
          <div key={func.name} className="text-xs">
            <code className="text-info">{func.signature}</code>
            <span className="ml-2 text-term-text-tertiary">
              {func.description}
            </span>
          </div>
        ))}
      </div>

      <div className="mb-3 mt-4 text-xs font-semibold text-term-text">
        Examples
      </div>
      <div className="space-y-2">
        {EXAMPLES.map((example) => (
          <div
            key={example.name}
            className="rounded border border-term-border-subtle bg-term-surface p-2"
          >
            <div className="text-xs font-semibold text-term-text">
              {example.name}
            </div>
            <code className="mt-1 block text-xs font-mono text-term-text-muted">
              {example.formula}
            </code>
            <p className="mt-1 text-xs text-term-text-tertiary">
              {example.description}
            </p>
          </div>
        ))}
      </div>
    </div>
  );
};

function validateAndTokenize(
  formula: string,
  availableFields: TargetField[],
): { validation: FormulaValidationResult } {
  const fieldRefs: TargetField[] = [];
  const functions: string[] = [];
  let isValid = true;
  let error: string | undefined;

  const regex = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
  let match: RegExpExecArray | null;

  while ((match = regex.exec(formula)) !== null) {
    const fieldName = match[1];
    if (availableFields.includes(fieldName as TargetField)) {
      fieldRefs.push(fieldName as TargetField);
    } else {
      isValid = false;
      error = `Unknown field: $${fieldName}`;
    }
  }

  const functionRegex = /\b([A-Z][A-Z0-9]*)\(/g;
  while ((match = functionRegex.exec(formula)) !== null) {
    const funcName = match[1];
    if (FUNCTIONS.some((f) => f.name === funcName)) {
      functions.push(funcName);
    }
  }

  return {
    validation: { isValid, error, fieldRefs, functions },
  };
}

const FUNCTIONS = [
  { name: "SUM", signature: "SUM(...)", description: "Sum of all values" },
  { name: "AVG", signature: "AVG(...)", description: "Average of all values" },
  { name: "MIN", signature: "MIN(...)", description: "Minimum value" },
  { name: "MAX", signature: "MAX(...)", description: "Maximum value" },
  { name: "ABS", signature: "ABS(value)", description: "Absolute value" },
  {
    name: "ROUND",
    signature: "ROUND(value, decimals)",
    description: "Round to decimals",
  },
  {
    name: "IF",
    signature: "IF(condition, then, else)",
    description: "Conditional expression",
  },
  { name: "SQRT", signature: "SQRT(value)", description: "Square root" },
  { name: "POW", signature: "POW(base, exponent)", description: "Power" },
  { name: "FLOOR", signature: "FLOOR(value)", description: "Round down" },
  { name: "CEIL", signature: "CEIL(value)", description: "Round up" },
];

const EXAMPLES = [
  {
    name: "Gross Margin %",
    formula: "($gross_profit / $total_revenues) * 100",
    description: "Calculate gross profit margin as percentage",
  },
  {
    name: "Operating Margin %",
    formula: "($operating_profit / $total_revenues) * 100",
    description: "Calculate operating margin as percentage",
  },
  {
    name: "Conditional Value",
    formula: "IF($total_revenues > 0, $net_income / $total_revenues, 0)",
    description: "Return ratio if revenue is positive, else 0",
  },
  {
    name: "Rounded Net Margin",
    formula: "ROUND($net_income / $total_revenues * 100, 2)",
    description: "Net margin rounded to 2 decimal places",
  },
];
