import { useState, useCallback } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command";
import { Plus, X, Check, ChevronsUpDown, Code, SlidersHorizontal } from "lucide-react";
import { cn } from "@/lib/utils";
import type { ColumnInfo } from "@/types";

// --- Types ---

type Conjunction = "AND" | "OR";

interface FilterCondition {
  id: string;
  column: string;
  operator: string;
  value: string;
  valueTo: string;
}

interface FilterBuilderProps {
  columns: ColumnInfo[];
  onFilterChange: (whereClause: string | undefined) => void;
  children?: React.ReactNode;
}

// --- Operator mapping by PG type ---

type TypeCategory =
  | "numeric"
  | "text"
  | "boolean"
  | "datetime"
  | "uuid"
  | "json"
  | "default";

const TYPE_CATEGORY_MAP: Record<string, TypeCategory> = {
  int2: "numeric",
  int4: "numeric",
  int8: "numeric",
  float4: "numeric",
  float8: "numeric",
  numeric: "numeric",
  decimal: "numeric",
  money: "numeric",
  serial: "numeric",
  bigserial: "numeric",
  smallserial: "numeric",
  varchar: "text",
  text: "text",
  char: "text",
  bpchar: "text",
  name: "text",
  citext: "text",
  bool: "boolean",
  boolean: "boolean",
  timestamp: "datetime",
  timestamptz: "datetime",
  date: "datetime",
  time: "datetime",
  timetz: "datetime",
  interval: "datetime",
  uuid: "uuid",
  json: "json",
  jsonb: "json",
};

const OPERATORS_BY_CATEGORY: Record<TypeCategory, string[]> = {
  numeric: ["=", "!=", ">", "<", ">=", "<=", "IS NULL", "IS NOT NULL", "IN", "BETWEEN"],
  text: ["=", "!=", "LIKE", "ILIKE", "IS NULL", "IS NOT NULL", "IN", "~"],
  boolean: ["=", "IS NULL", "IS NOT NULL"],
  datetime: ["=", "!=", ">", "<", ">=", "<=", "IS NULL", "IS NOT NULL", "BETWEEN"],
  uuid: ["=", "!=", "IS NULL", "IS NOT NULL", "IN"],
  json: ["IS NULL", "IS NOT NULL"],
  default: ["=", "!=", "IS NULL", "IS NOT NULL"],
};

// --- Helpers ---

function getTypeCategory(dataType: string): TypeCategory {
  return TYPE_CATEGORY_MAP[dataType] ?? "default";
}

function getOperatorsForColumn(dataType: string): string[] {
  return OPERATORS_BY_CATEGORY[getTypeCategory(dataType)];
}

function quoteIdentifier(name: string): string {
  return `"${name.replace(/"/g, '""')}"`;
}

function escapeLiteral(value: string): string {
  return `'${value.replace(/'/g, "''")}'`;
}

function serializeConditions(
  conditions: FilterCondition[],
  conjunction: Conjunction,
  columns: ColumnInfo[],
): string | undefined {
  const parts: string[] = [];

  for (const c of conditions) {
    if (!c.column) continue;

    const col = quoteIdentifier(c.column);
    const colInfo = columns.find((ci) => ci.name === c.column);
    const isBool = colInfo && getTypeCategory(colInfo.data_type) === "boolean";

    if (c.operator === "IS NULL") {
      parts.push(`${col} IS NULL`);
    } else if (c.operator === "IS NOT NULL") {
      parts.push(`${col} IS NOT NULL`);
    } else if (c.operator === "IN") {
      const items = c.value
        .split(",")
        .map((v) => v.trim())
        .filter(Boolean)
        .map((v) => escapeLiteral(v));
      if (items.length > 0) {
        parts.push(`${col} IN (${items.join(", ")})`);
      }
    } else if (c.operator === "BETWEEN") {
      if (c.value && c.valueTo) {
        parts.push(
          `${col} BETWEEN ${escapeLiteral(c.value)} AND ${escapeLiteral(c.valueTo)}`,
        );
      }
    } else if (isBool) {
      if (c.value === "true" || c.value === "false") {
        parts.push(`${col} ${c.operator} ${c.value}`);
      }
    } else {
      if (c.value !== "") {
        parts.push(`${col} ${c.operator} ${escapeLiteral(c.value)}`);
      }
    }
  }

  if (parts.length === 0) return undefined;
  return parts.join(` ${conjunction} `);
}

function createCondition(): FilterCondition {
  return {
    id: crypto.randomUUID(),
    column: "",
    operator: "=",
    value: "",
    valueTo: "",
  };
}

// --- Sub-components ---

function ColumnCombobox({
  columns,
  value,
  onSelect,
}: {
  columns: ColumnInfo[];
  value: string;
  onSelect: (column: string) => void;
}) {
  const [open, setOpen] = useState(false);
  const selected = columns.find((c) => c.name === value);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          className="h-6 w-[130px] justify-between px-2 text-xs font-normal"
        >
          <span className="truncate">
            {selected ? selected.name : "Column..."}
          </span>
          <ChevronsUpDown className="ml-1 h-3 w-3 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-[220px] p-0" align="start">
        <Command>
          <CommandInput placeholder="Search column..." className="text-xs" />
          <CommandList>
            <CommandEmpty>No column found.</CommandEmpty>
            <CommandGroup>
              {columns.map((col) => (
                <CommandItem
                  key={col.name}
                  value={col.name}
                  onSelect={(v) => {
                    onSelect(v);
                    setOpen(false);
                  }}
                >
                  <Check
                    className={cn(
                      "mr-1 h-3 w-3",
                      value === col.name ? "opacity-100" : "opacity-0",
                    )}
                  />
                  <span className="truncate">{col.name}</span>
                  <span className="ml-auto text-[10px] text-muted-foreground">
                    {col.data_type}
                  </span>
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}

function FilterConditionRow({
  condition,
  columns,
  onChange,
  onRemove,
}: {
  condition: FilterCondition;
  columns: ColumnInfo[];
  onChange: (updated: FilterCondition) => void;
  onRemove: () => void;
}) {
  const colInfo = columns.find((c) => c.name === condition.column);
  const dataType = colInfo?.data_type ?? "";
  const operators = getOperatorsForColumn(dataType);
  const category = getTypeCategory(dataType);
  const needsNoValue =
    condition.operator === "IS NULL" || condition.operator === "IS NOT NULL";
  const isBetween = condition.operator === "BETWEEN";
  const isBoolEq = category === "boolean" && condition.operator === "=";

  const handleColumnChange = (columnName: string) => {
    const newColInfo = columns.find((c) => c.name === columnName);
    const newType = newColInfo?.data_type ?? "";
    const newOps = getOperatorsForColumn(newType);
    const newOperator = newOps.includes(condition.operator)
      ? condition.operator
      : newOps[0];
    onChange({
      ...condition,
      column: columnName,
      operator: newOperator,
      value: "",
      valueTo: "",
    });
  };

  return (
    <div className="flex items-center gap-1">
      <ColumnCombobox
        columns={columns}
        value={condition.column}
        onSelect={handleColumnChange}
      />

      <Select
        value={condition.operator}
        onValueChange={(op) =>
          onChange({ ...condition, operator: op, value: "", valueTo: "" })
        }
      >
        <SelectTrigger className="h-6 w-[100px] px-2 text-xs">
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          {operators.map((op) => (
            <SelectItem key={op} value={op} className="text-xs">
              {op}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>

      {!needsNoValue && (
        isBoolEq ? (
          <Select
            value={condition.value}
            onValueChange={(v) => onChange({ ...condition, value: v })}
          >
            <SelectTrigger className="h-6 w-[80px] px-2 text-xs">
              <SelectValue placeholder="value" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="true" className="text-xs">
                true
              </SelectItem>
              <SelectItem value="false" className="text-xs">
                false
              </SelectItem>
            </SelectContent>
          </Select>
        ) : isBetween ? (
          <>
            <Input
              className="h-6 w-[80px] text-xs"
              placeholder="from"
              value={condition.value}
              onChange={(e) => onChange({ ...condition, value: e.target.value })}
            />
            <span className="text-[10px] text-muted-foreground">—</span>
            <Input
              className="h-6 w-[80px] text-xs"
              placeholder="to"
              value={condition.valueTo}
              onChange={(e) =>
                onChange({ ...condition, valueTo: e.target.value })
              }
            />
          </>
        ) : (
          <Input
            className="h-6 w-[120px] text-xs"
            placeholder={
              condition.operator === "IN" ? "val1, val2, ..." : "value"
            }
            value={condition.value}
            onChange={(e) => onChange({ ...condition, value: e.target.value })}
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.currentTarget.form?.requestSubmit();
              }
            }}
          />
        )
      )}

      <Button
        variant="ghost"
        size="sm"
        className="h-5 w-5 p-0 text-muted-foreground hover:text-destructive"
        onClick={onRemove}
      >
        <X className="h-3 w-3" />
      </Button>
    </div>
  );
}

// --- Main component ---

type FilterMode = "visual" | "sql";

export function FilterBuilder({ columns, onFilterChange, children }: FilterBuilderProps) {
  const [mode, setMode] = useState<FilterMode>("visual");
  const [conditions, setConditions] = useState<FilterCondition[]>([]);
  const [conjunction, setConjunction] = useState<Conjunction>("AND");
  const [rawFilter, setRawFilter] = useState("");

  const handleAdd = useCallback(() => {
    setConditions((prev) => [...prev, createCondition()]);
  }, []);

  const handleRemove = useCallback((id: string) => {
    setConditions((prev) => prev.filter((c) => c.id !== id));
  }, []);

  const handleChange = useCallback((updated: FilterCondition) => {
    setConditions((prev) =>
      prev.map((c) => (c.id === updated.id ? updated : c)),
    );
  }, []);

  const handleApplyVisual = useCallback(() => {
    const clause = serializeConditions(conditions, conjunction, columns);
    onFilterChange(clause);
  }, [conditions, conjunction, columns, onFilterChange]);

  const handleApplyRaw = useCallback(() => {
    let clause = rawFilter.trim();
    if (clause.toLowerCase().startsWith("where ")) {
      clause = clause.slice(6).trim();
    }
    onFilterChange(clause || undefined);
  }, [rawFilter, onFilterChange]);

  const handleClear = useCallback(() => {
    setConditions([]);
    setRawFilter("");
    onFilterChange(undefined);
  }, [onFilterChange]);

  const hasActiveFilter =
    mode === "visual" ? conditions.length > 0 : rawFilter.trim() !== "";

  return (
    <>
      {/* Toolbar row */}
      <div className="flex items-center gap-2 border-b px-2 py-1">
        {/* Mode toggle */}
        <div className="flex items-center rounded-md border text-xs">
          <button
            className={cn(
              "flex items-center gap-1 px-1.5 py-0.5 font-medium",
              mode === "visual"
                ? "bg-muted text-foreground"
                : "text-muted-foreground hover:text-foreground",
            )}
            onClick={() => setMode("visual")}
            title="Visual filter builder"
          >
            <SlidersHorizontal className="h-3 w-3" />
            Filter
          </button>
          <button
            className={cn(
              "flex items-center gap-1 px-1.5 py-0.5 font-medium",
              mode === "sql"
                ? "bg-muted text-foreground"
                : "text-muted-foreground hover:text-foreground",
            )}
            onClick={() => setMode("sql")}
            title="Raw SQL WHERE clause"
          >
            <Code className="h-3 w-3" />
            SQL
          </button>
        </div>

        {/* Visual mode controls */}
        {mode === "visual" && (
          <>
            <Button
              variant="ghost"
              size="sm"
              className="h-6 gap-1 text-xs"
              onClick={handleAdd}
            >
              <Plus className="h-3 w-3" />
              {conditions.length === 0 ? "Add filter" : "Add"}
            </Button>
            {conditions.length > 0 && (
              <>
                {conditions.length >= 2 && (
                  <div className="flex items-center rounded-md border text-[10px]">
                    <button
                      className={cn(
                        "px-1.5 py-0.5 font-medium",
                        conjunction === "AND"
                          ? "bg-muted text-foreground"
                          : "text-muted-foreground hover:text-foreground",
                      )}
                      onClick={() => setConjunction("AND")}
                    >
                      AND
                    </button>
                    <button
                      className={cn(
                        "px-1.5 py-0.5 font-medium",
                        conjunction === "OR"
                          ? "bg-muted text-foreground"
                          : "text-muted-foreground hover:text-foreground",
                      )}
                      onClick={() => setConjunction("OR")}
                    >
                      OR
                    </button>
                  </div>
                )}
                <Button
                  size="sm"
                  variant="ghost"
                  className="h-6 text-xs"
                  onClick={handleApplyVisual}
                >
                  Apply
                </Button>
              </>
            )}
          </>
        )}

        {/* SQL mode controls */}
        {mode === "sql" && (
          <>
            <Input
              placeholder="e.g. id > 10 AND status = 'active'"
              className="h-6 max-w-md flex-1 text-xs"
              value={rawFilter}
              onChange={(e) => setRawFilter(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && handleApplyRaw()}
            />
            <Button
              size="sm"
              variant="ghost"
              className="h-6 text-xs"
              onClick={handleApplyRaw}
            >
              Apply
            </Button>
          </>
        )}

        {hasActiveFilter && (
          <Button
            size="sm"
            variant="ghost"
            className="h-6 text-xs text-muted-foreground"
            onClick={handleClear}
          >
            Clear
          </Button>
        )}

        {children}
      </div>

      {/* Visual mode: condition rows below the toolbar */}
      {mode === "visual" && conditions.length > 0 && (
        <div className="flex flex-col gap-1 border-b bg-muted/30 px-2 py-1.5">
          {conditions.map((c) => (
            <FilterConditionRow
              key={c.id}
              condition={c}
              columns={columns}
              onChange={handleChange}
              onRemove={() => handleRemove(c.id)}
            />
          ))}
        </div>
      )}
    </>
  );
}
