import { useEffect, useMemo, useRef, useState } from "react";
import type { JsonObject, JsonValue } from "../editorCore";

type ValueFilterOperator = "contains" | "equals" | "starts_with" | "is_empty" | "is_not_empty";

type ValueFilterRule = {
  id: string;
  key: string;
  operator: ValueFilterOperator;
  value: string;
};

type RecordListPanelProps = {
  activeType: string;
  records: JsonObject[];
  selectedIndex: number;
  isAllRecordsSelected: boolean;
  hasPendingRecordChanges: boolean;
  getRecordLabel: (record: JsonObject, index: number) => string;
  buildSpritePreviewDataUrl: (record: JsonObject, pixelSize: number) => string | null;
  normalizeHexColor: (value: JsonValue | undefined, fallback?: string) => string;
  onSelectRecordIndex: (index: number) => void;
  onCommitPendingRecordChanges: () => void;
  onRevertPendingRecordChanges: () => void;
  recordDraftError: string;
  isLoading: boolean;
  isSaving: boolean;
};

export default function RecordListPanel(props: RecordListPanelProps) {
  const {
    activeType,
    records,
    selectedIndex,
    isAllRecordsSelected,
    hasPendingRecordChanges,
    getRecordLabel,
    buildSpritePreviewDataUrl,
    normalizeHexColor,
    onSelectRecordIndex,
    onCommitPendingRecordChanges,
    onRevertPendingRecordChanges,
    recordDraftError,
    isLoading,
    isSaving,
  } = props;

  const npcTemplateFilterKeys = useMemo(() => {
    const preferredOrder = [
      "id",
      "name",
      "description",
      "title",
      "faction",
      "spriteId",
      "defaultDialogueId",
      "job",
      "shopInventoryId",
      "lootTableId",
    ];
    if (activeType !== "npc_templates") {
      return [] as string[];
    }
    return preferredOrder;
  }, [activeType]);

  const [recordSearchQuery, setRecordSearchQuery] = useState("");
  const [isFilterMenuOpen, setIsFilterMenuOpen] = useState(false);
  const [valueRules, setValueRules] = useState<ValueFilterRule[]>([]);
  const [draftRuleKey, setDraftRuleKey] = useState<string>("");
  const [draftRuleOperator, setDraftRuleOperator] = useState<ValueFilterOperator>("contains");
  const [draftRuleValue, setDraftRuleValue] = useState("");
  const filterDropdownRef = useRef<HTMLDivElement | null>(null);
  const isNpcTemplateFilterMode = activeType === "npc_templates";
  const activeRecordSearchQuery = isNpcTemplateFilterMode ? recordSearchQuery : "";
  const activeValueRules = useMemo(() => (isNpcTemplateFilterMode ? valueRules : []), [isNpcTemplateFilterMode, valueRules]);
  const effectiveDraftRuleKey = useMemo(() => {
    if (!isNpcTemplateFilterMode) {
      return "";
    }
    if (draftRuleKey && npcTemplateFilterKeys.includes(draftRuleKey)) {
      return draftRuleKey;
    }
    return npcTemplateFilterKeys[0] || "";
  }, [draftRuleKey, isNpcTemplateFilterMode, npcTemplateFilterKeys]);
  const effectiveDraftRuleOperator = isNpcTemplateFilterMode ? draftRuleOperator : "contains";
  const effectiveDraftRuleValue = isNpcTemplateFilterMode ? draftRuleValue : "";
  const isFilterMenuVisible = isNpcTemplateFilterMode && isFilterMenuOpen;

  useEffect(() => {
    if (!isFilterMenuVisible) {
      return;
    }
    function handleOutsideClick(event: MouseEvent): void {
      if (!filterDropdownRef.current) {
        return;
      }
      const target = event.target as Node | null;
      if (target && filterDropdownRef.current.contains(target)) {
        return;
      }
      setIsFilterMenuOpen(false);
    }
    window.addEventListener("mousedown", handleOutsideClick);
    return () => window.removeEventListener("mousedown", handleOutsideClick);
  }, [isFilterMenuVisible]);

  const valueSuggestions = useMemo(() => {
    if (!isNpcTemplateFilterMode || !effectiveDraftRuleKey) {
      return [] as Array<{ value: string; count: number }>;
    }
    const countByValue = new Map<string, number>();
    records.forEach((record) => {
      const raw = record[effectiveDraftRuleKey];
      if (Array.isArray(raw) || (raw !== null && typeof raw === "object")) {
        return;
      }
      const nextValue = String(raw ?? "").trim();
      if (!nextValue) {
        return;
      }
      countByValue.set(nextValue, (countByValue.get(nextValue) || 0) + 1);
    });
    return Array.from(countByValue.entries())
      .map(([value, count]) => ({ value, count }))
      .sort((a, b) => (b.count - a.count) || a.value.localeCompare(b.value))
      .slice(0, 8);
  }, [effectiveDraftRuleKey, isNpcTemplateFilterMode, records]);

  function matchesRule(record: JsonObject, rule: ValueFilterRule): boolean {
    const raw = record[rule.key];
    const normalized = (Array.isArray(raw) || (raw !== null && typeof raw === "object"))
      ? ""
      : String(raw ?? "");
    const source = normalized.toLowerCase();
    const expected = rule.value.toLowerCase();
    if (rule.operator === "is_empty") {
      return source.trim().length === 0;
    }
    if (rule.operator === "is_not_empty") {
      return source.trim().length > 0;
    }
    if (rule.operator === "equals") {
      return source === expected;
    }
    if (rule.operator === "starts_with") {
      return source.startsWith(expected);
    }
    return source.includes(expected);
  }

  const filteredRecords = useMemo(() => {
    if (!isNpcTemplateFilterMode) {
      return records.map((record, index) => ({ record, index }));
    }

    const query = activeRecordSearchQuery.trim().toLowerCase();
    const keySet = new Set(npcTemplateFilterKeys);

    const list = records.map((record, index) => ({ record, index }));
    return list.filter(({ record }) => {
      const passesSearch = !query || Object.entries(record || {}).some(([key, value]) => {
        if (!keySet.has(key)) {
          return false;
        }
        if (Array.isArray(value) || (value !== null && typeof value === "object")) {
          return false;
        }
        return String(value ?? "").toLowerCase().includes(query);
      });

      if (!passesSearch) {
        return false;
      }
      return activeValueRules.every((rule) => matchesRule(record, rule));
    });
  }, [activeRecordSearchQuery, activeValueRules, isNpcTemplateFilterMode, npcTemplateFilterKeys, records]);

  function addValueRule(): void {
    if (!isNpcTemplateFilterMode || !effectiveDraftRuleKey) {
      return;
    }
    if (
      effectiveDraftRuleOperator !== "is_empty"
      && effectiveDraftRuleOperator !== "is_not_empty"
      && !effectiveDraftRuleValue.trim()
    ) {
      return;
    }
    setValueRules((prev) => [
      ...prev,
      {
        id: `rule_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,
        key: effectiveDraftRuleKey,
        operator: effectiveDraftRuleOperator,
        value: effectiveDraftRuleValue.trim(),
      },
    ]);
    setDraftRuleValue("");
  }

  function removeValueRule(id: string): void {
    setValueRules((prev) => prev.filter((rule) => rule.id !== id));
  }

  function clearAllFilters(): void {
    setRecordSearchQuery("");
    setValueRules([]);
    setDraftRuleOperator("contains");
    setDraftRuleValue("");
  }

  function operatorLabel(operator: ValueFilterOperator): string {
    if (operator === "starts_with") return "starts with";
    if (operator === "is_empty") return "is empty";
    if (operator === "is_not_empty") return "is not empty";
    return operator;
  }

  return (
    <aside className="record-list-panel">
      <div className="records-head">
        <h2>Records</h2>
        {hasPendingRecordChanges ? (
          <div className="record-actions">
            <button type="button" className="success" onClick={onCommitPendingRecordChanges} disabled={Boolean(recordDraftError) || isLoading || isSaving}>Commit</button>
            <button type="button" className="danger" onClick={onRevertPendingRecordChanges} disabled={isLoading || isSaving}>Revert</button>
          </div>
        ) : null}
      </div>
      {activeType === "npc_templates" ? (
        <div className="record-list-tools">
          <input
            type="text"
            className="record-list-search"
            placeholder="Search NPC records..."
            value={activeRecordSearchQuery}
            onChange={(event) => setRecordSearchQuery(event.target.value)}
          />
          <div className="record-filter-dropdown" ref={filterDropdownRef}>
            <button
              type="button"
              className="record-filter-toggle"
              onClick={() => setIsFilterMenuOpen((prev) => !prev)}
            >
              Filters ({activeValueRules.length})
            </button>
            <button
              type="button"
              className="record-filter-clear"
              onClick={clearAllFilters}
              disabled={!activeRecordSearchQuery && activeValueRules.length === 0}
            >
              Clear
            </button>
            {isFilterMenuVisible ? (
              <div className="record-filter-menu">
                <div className="record-filter-section-title">Value Rule</div>
                <div className="record-rule-builder">
                  <select value={effectiveDraftRuleKey} onChange={(event) => setDraftRuleKey(event.target.value)}>
                    {npcTemplateFilterKeys.map((key) => (
                      <option key={`rule-key-${key}`} value={key}>{key}</option>
                    ))}
                  </select>
                  <select
                    value={effectiveDraftRuleOperator}
                    onChange={(event) => setDraftRuleOperator(event.target.value as ValueFilterOperator)}
                  >
                    <option value="contains">contains</option>
                    <option value="equals">equals</option>
                    <option value="starts_with">starts with</option>
                    <option value="is_empty">is empty</option>
                    <option value="is_not_empty">is not empty</option>
                  </select>
                  {effectiveDraftRuleOperator !== "is_empty" && effectiveDraftRuleOperator !== "is_not_empty" ? (
                    <input
                      type="text"
                      value={effectiveDraftRuleValue}
                      onChange={(event) => setDraftRuleValue(event.target.value)}
                      onKeyDown={(event) => {
                        if (event.key === "Enter") {
                          event.preventDefault();
                          addValueRule();
                        }
                      }}
                      placeholder="value"
                    />
                  ) : null}
                  <button type="button" className="record-rule-add-btn" onClick={addValueRule}>+ Add Rule</button>
                </div>
                {valueSuggestions.length > 0 && effectiveDraftRuleOperator !== "is_empty" && effectiveDraftRuleOperator !== "is_not_empty" ? (
                  <div className="record-suggestion-list">
                    {valueSuggestions.map((entry) => (
                      <button
                        key={`rule-suggestion-${entry.value}`}
                        type="button"
                        className="record-suggestion-chip"
                        onClick={() => setDraftRuleValue(entry.value)}
                      >
                        {entry.value} ({entry.count})
                      </button>
                    ))}
                  </div>
                ) : null}
              </div>
            ) : null}
          </div>
        </div>
      ) : null}
      {activeType === "npc_templates" && activeValueRules.length > 0 ? (
        <div className="active-rule-list">
          {activeValueRules.map((rule) => (
            <button
              key={rule.id}
              type="button"
              className="active-rule-chip"
              onClick={() => removeValueRule(rule.id)}
              title="Remove rule"
            >
              {rule.key} {operatorLabel(rule.operator)}{rule.operator === "is_empty" || rule.operator === "is_not_empty" ? "" : ` ${rule.value}`} x
            </button>
          ))}
        </div>
      ) : null}
      <div className="record-list">
        <button
          type="button"
          className={`record-row ${isAllRecordsSelected ? "is-active" : ""} ${hasPendingRecordChanges && isAllRecordsSelected ? "has-pending" : ""}`}
          onClick={() => onSelectRecordIndex(-1)}
        >
          All
        </button>
        {filteredRecords.length === 0 ? <p className="muted">No records match the current filters.</p> : null}
        {filteredRecords.map(({ record, index }) => (
          (() => {
            const previewUrl = (activeType === "sprites" || activeType === "tiles") ? buildSpritePreviewDataUrl(record, 2) : null;
            return (
              <button
                key={`${activeType}-${index}`}
                type="button"
                className={`record-row ${index === selectedIndex ? "is-active" : ""} ${hasPendingRecordChanges && index === selectedIndex ? "has-pending" : ""}`}
                onClick={() => onSelectRecordIndex(index)}
              >
                {activeType === "sprites" || activeType === "tiles" ? (
                  <span className="record-row-with-thumb">
                    {previewUrl ? (
                      <img
                        className="sprite-thumb sprite-thumb-list"
                        src={previewUrl}
                        alt=""
                        aria-hidden="true"
                      />
                    ) : (
                      <span className="sprite-thumb sprite-thumb-list sprite-thumb-empty" aria-hidden="true" />
                    )}
                    <span>{getRecordLabel(record, index)}</span>
                  </span>
                ) : activeType === "factions" ? (
                  <span className="record-row-with-thumb">
                    <span className="faction-record-label" style={{ color: normalizeHexColor(record.color) }}>
                      {getRecordLabel(record, index)}
                    </span>
                  </span>
                ) : (
                  getRecordLabel(record, index)
                )}
              </button>
            );
          })()
        ))}
      </div>
    </aside>
  );
}
