"use client";

import { useState } from "react";
import { useRules, useCreateRule, useUpdateRule, useDeleteRule, useApplyRules, useRuleRuns, useRevertRuleRun, useTags, useParticipants } from "@/lib/hooks";
import { CATEGORIES, formatCategory } from "@/lib/categories";
import { RulePreviewModal } from "@/components/rule-preview-modal";
import { RuleRunDetail } from "@/components/rule-run-detail";

const FIELDS = [
  { value: "merchant_normalized", label: "Merchant" },
  { value: "description", label: "Description" },
  { value: "category", label: "Category" },
  { value: "bank_name", label: "Bank" },
  { value: "amount", label: "Amount" },
  { value: "transaction_type", label: "Transaction Type" },
  { value: "tag", label: "Tag" },
] as const;

const TEXT_OPS = [
  { value: "contains", label: "contains" },
  { value: "equals", label: "equals" },
  { value: "starts_with", label: "starts with" },
  { value: "not_equals", label: "not equals" },
];
const AMOUNT_OPS = [
  { value: "equals", label: "=" },
  { value: "not_equals", label: "≠" },
  { value: "gt", label: ">" },
  { value: "lt", label: "<" },
];
const ENUM_OPS = [
  { value: "equals", label: "equals" },
  { value: "not_equals", label: "not equals" },
];
const TRANSACTION_TYPES = ["debit", "credit", "payment", "refund", "fee", "interest", "transfer"];

type Condition = { field: string; operator: string; value: string };
type SplitEntry = { participant_id: number; share_percent: number };
type Actions = { set_category?: string; add_tag_ids?: number[]; set_merchant?: string; apply_split?: SplitEntry[] };

function humanCondition(c: Condition, tagNames?: Map<number, string>): string {
  const fieldLabel = FIELDS.find((f) => f.value === c.field)?.label || c.field;
  if (c.field === "tag") {
    const tagName = tagNames?.get(Number(c.value)) || `tag#${c.value}`;
    return `Tag ${c.operator === "not_equals" ? "is not" : "is"} "${tagName}"`;
  }
  const ops = [...TEXT_OPS, ...AMOUNT_OPS, ...ENUM_OPS];
  const opText = ops.find((o) => o.value === c.operator)?.label || c.operator;
  return `${fieldLabel} ${opText} "${c.value}"`;
}

function humanAction(a: Actions, tagNames: Map<number, string>, participantNames: Map<number, string>): string {
  const parts: string[] = [];
  if (a.set_category) parts.push(`set category: ${formatCategory(a.set_category)}`);
  if (a.set_merchant) parts.push(`set merchant: ${a.set_merchant}`);
  if (a.add_tag_ids?.length) {
    const names = a.add_tag_ids.map((id) => tagNames.get(id) || `tag#${id}`).join(", ");
    parts.push(`add tags: ${names}`);
  }
  if (a.apply_split?.length) {
    const splits = a.apply_split.map((s) => `${participantNames.get(s.participant_id) || `#${s.participant_id}`} ${s.share_percent}%`).join(", ");
    parts.push(`split: ${splits}`);
  }
  return parts.length ? "→ " + parts.join(", ") : "(no actions)";
}

const EMPTY_ACTIONS: Actions = {};

export default function RulesPage() {
  const { data: rules = [], isLoading } = useRules();
  const { data: tags = [] } = useTags();
  const { data: participants = [] } = useParticipants();
  const createRule = useCreateRule();
  const updateRule = useUpdateRule();
  const deleteRule = useDeleteRule();
  const applyRules = useApplyRules();
  const { data: runs = [] } = useRuleRuns();
  const revertRun = useRevertRuleRun();

  const tagNames = new Map(tags.map((t) => [t.id, t.name]));
  const participantNames = new Map(participants.map((p) => [p.id, p.name]));

  const [applyFrom, setApplyFrom] = useState("2026-01-09");
  const [showForm, setShowForm] = useState(false);
  const [editingId, setEditingId] = useState<number | null>(null);
  const [applyResult, setApplyResult] = useState<{ matched: number; transactions_affected: number } | null>(null);
  const [preview, setPreview] = useState<{ id: number; name: string } | null>(null);
  const [expandedRun, setExpandedRun] = useState<number | null>(null);
  const [name, setName] = useState("");
  const [conditions, setConditions] = useState<Condition[]>([]);
  const [actions, setActions] = useState<Actions>(EMPTY_ACTIONS);
  const [priority, setPriority] = useState(0);
  const [manualOnly, setManualOnly] = useState(false);

  function openNewForm() {
    setEditingId(null);
    setName("");
    setConditions([]);
    setActions(EMPTY_ACTIONS);
    setPriority(0);
    setManualOnly(false);
    setShowForm(true);
  }

  function openEditForm(rule: { id: number; name: string; conditions: Condition[]; actions: Actions; priority: number; manual_only?: boolean }) {
    setEditingId(rule.id);
    setName(rule.name);
    setConditions(Array.isArray(rule.conditions) ? rule.conditions : []);
    setActions(rule.actions && typeof rule.actions === "object" ? rule.actions : EMPTY_ACTIONS);
    setPriority(rule.priority);
    setManualOnly(!!rule.manual_only);
    setShowForm(true);
    window.scrollTo({ top: 0, behavior: "smooth" });
  }

  function closeForm() {
    setShowForm(false);
    setEditingId(null);
  }

  function addCondition() {
    setConditions([...conditions, { field: "merchant_normalized", operator: "contains", value: "" }]);
  }

  function updateCondition(i: number, patch: Partial<Condition>) {
    setConditions(conditions.map((c, idx) => (idx === i ? { ...c, ...patch } : c)));
  }

  function removeCondition(i: number) {
    setConditions(conditions.filter((_, idx) => idx !== i));
  }

  function addSplitEntry() {
    if (!participants.length) return;
    const existing = actions.apply_split || [];
    setActions({ ...actions, apply_split: [...existing, { participant_id: participants[0].id, share_percent: 0 }] });
  }

  function updateSplitEntry(i: number, patch: Partial<SplitEntry>) {
    const entries = (actions.apply_split || []).map((s, idx) => (idx === i ? { ...s, ...patch } : s));
    setActions({ ...actions, apply_split: entries });
  }

  function removeSplitEntry(i: number) {
    const entries = (actions.apply_split || []).filter((_, idx) => idx !== i);
    setActions({ ...actions, apply_split: entries.length ? entries : undefined });
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    const payload = { name, conditions, actions, enabled: true, manual_only: manualOnly, priority };
    if (editingId !== null) {
      await updateRule.mutateAsync({ id: editingId, ...payload });
    } else {
      await createRule.mutateAsync(payload);
    }
    closeForm();
  }

  async function handleApply(ruleId?: number) {
    const result = await applyRules.mutateAsync({ splitFrom: applyFrom || undefined, ruleId });
    setApplyResult(result);
  }

  const splitTotal = (actions.apply_split || []).reduce((sum, s) => sum + (s.share_percent || 0), 0);
  const isPending = editingId !== null ? updateRule.isPending : createRule.isPending;

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <h2 className="text-2xl font-display">Rules</h2>
        <div className="flex gap-2 items-center">
          <label className="text-xs text-zinc-500 whitespace-nowrap">Splits from</label>
          <input
            type="date"
            value={applyFrom}
            onChange={(e) => setApplyFrom(e.target.value)}
            className="bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
            title="Split rules only apply to transactions on or after this date. Category/merchant/tag rules apply to all transactions."
          />
          <button
            onClick={() => handleApply()}
            disabled={applyRules.isPending}
            className="px-4 py-2 bg-emerald-600 hover:bg-emerald-500 text-white rounded-lg text-sm font-medium disabled:opacity-50"
          >
            {applyRules.isPending ? "Applying..." : "Apply All Rules"}
          </button>
          <button
            onClick={showForm ? closeForm : openNewForm}
            className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg text-sm font-medium"
          >
            {showForm ? "Cancel" : "New Rule"}
          </button>
        </div>
      </div>

      {applyResult && (
        <div className="p-4 bg-emerald-900/30 border border-emerald-700 rounded-lg text-sm">
          Applied: <strong>{applyResult.matched}</strong> condition matches across{" "}
          <strong>{applyResult.transactions_affected}</strong> transactions.
          <button onClick={() => setApplyResult(null)} className="ml-4 text-zinc-400 hover:text-white">
            dismiss
          </button>
        </div>
      )}

      {showForm && (
        <form onSubmit={handleSubmit} className="bg-zinc-900 border border-zinc-700 rounded-xl p-6 space-y-4">
          <h3 className="font-semibold text-sm text-zinc-300">{editingId !== null ? "Edit Rule" : "New Rule"}</h3>

          <div>
            <label className="block text-xs text-zinc-500 mb-1">Rule Name</label>
            <input
              value={name}
              onChange={(e) => setName(e.target.value)}
              required
              placeholder="e.g. Tag Woolworths as groceries"
              className="w-full bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-2 text-sm"
            />
          </div>

          <div>
            <div className="flex items-center justify-between mb-2">
              <label className="text-xs text-zinc-500">Conditions (ALL must match)</label>
              <button type="button" onClick={addCondition} className="text-xs text-indigo-400 hover:text-indigo-300">
                + Add condition
              </button>
            </div>
            {conditions.map((cond, i) => {
              const isAmount = cond.field === "amount";
              const isEnum = cond.field === "transaction_type";
              const isTag = cond.field === "tag";
              const ops = isAmount ? AMOUNT_OPS : (isEnum || isTag) ? ENUM_OPS : TEXT_OPS;
              return (
                <div key={i} className="flex gap-2 mb-2 items-center">
                  <select
                    value={cond.field}
                    onChange={(e) => {
                      const newField = e.target.value;
                      const patch: Partial<Condition> = { field: newField };
                      if (newField === "amount") { patch.operator = "equals"; patch.value = ""; }
                      else if (newField === "transaction_type") { patch.operator = "equals"; patch.value = "debit"; }
                      else if (newField === "tag") { patch.operator = "equals"; patch.value = tags[0] ? String(tags[0].id) : ""; }
                      else { patch.operator = "contains"; patch.value = ""; }
                      updateCondition(i, patch);
                    }}
                    className="bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                  >
                    {FIELDS.map((f) => (
                      <option key={f.value} value={f.value}>
                        {f.label}
                      </option>
                    ))}
                  </select>
                  <select
                    value={cond.operator}
                    onChange={(e) => updateCondition(i, { operator: e.target.value })}
                    className="bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                  >
                    {ops.map((o) => (
                      <option key={o.value} value={o.value}>
                        {o.label}
                      </option>
                    ))}
                  </select>
                  {isTag ? (
                    <select
                      value={cond.value}
                      onChange={(e) => updateCondition(i, { value: e.target.value })}
                      className="flex-1 bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                    >
                      {tags.map((t) => (
                        <option key={t.id} value={String(t.id)}>{t.name}</option>
                      ))}
                      {tags.length === 0 && <option value="">No tags</option>}
                    </select>
                  ) : isEnum ? (
                    <select
                      value={cond.value}
                      onChange={(e) => updateCondition(i, { value: e.target.value })}
                      className="flex-1 bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                    >
                      {TRANSACTION_TYPES.map((t) => (
                        <option key={t} value={t}>{t}</option>
                      ))}
                    </select>
                  ) : (
                    <input
                      value={cond.value}
                      onChange={(e) => updateCondition(i, { value: e.target.value })}
                      placeholder="value"
                      className="flex-1 bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                    />
                  )}
                  <button
                    type="button"
                    onClick={() => removeCondition(i)}
                    className="text-zinc-500 hover:text-red-400 text-lg leading-none px-1"
                  >
                    ×
                  </button>
                </div>
              );
            })}
            {conditions.length === 0 && (
              <p className="text-xs text-zinc-600">No conditions — rule will match ALL transactions.</p>
            )}
          </div>

          <div className="grid grid-cols-2 gap-4">
            <div>
              <label className="block text-xs text-zinc-500 mb-1">Set Category (optional)</label>
              <select
                value={actions.set_category || ""}
                onChange={(e) => setActions({ ...actions, set_category: e.target.value || undefined })}
                className="w-full bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-2 text-sm"
              >
                <option value="">— no change —</option>
                {CATEGORIES.map((c) => (
                  <option key={c} value={c}>
                    {formatCategory(c)}
                  </option>
                ))}
              </select>
            </div>
            <div>
              <label className="block text-xs text-zinc-500 mb-1">Set Merchant (optional)</label>
              <input
                value={actions.set_merchant || ""}
                onChange={(e) => setActions({ ...actions, set_merchant: e.target.value || undefined })}
                placeholder="Normalized name"
                className="w-full bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-2 text-sm"
              />
            </div>
          </div>

          <div>
            <label className="block text-xs text-zinc-500 mb-1">Add Tags (optional)</label>
            <div className="flex flex-wrap gap-2">
              {tags.map((tag) => {
                const selected = (actions.add_tag_ids || []).includes(tag.id);
                return (
                  <button
                    key={tag.id}
                    type="button"
                    onClick={() => {
                      const ids = actions.add_tag_ids || [];
                      setActions({
                        ...actions,
                        add_tag_ids: selected ? ids.filter((id) => id !== tag.id) : [...ids, tag.id],
                      });
                    }}
                    className={`px-2 py-1 rounded text-xs border transition-colors ${
                      selected ? "border-transparent text-white" : "border-zinc-700 text-zinc-400"
                    }`}
                    style={selected ? { backgroundColor: tag.color } : {}}
                  >
                    {tag.name}
                  </button>
                );
              })}
              {tags.length === 0 && <p className="text-xs text-zinc-600">No tags created yet.</p>}
            </div>
          </div>

          <div>
            <div className="flex items-center justify-between mb-2">
              <label className="text-xs text-zinc-500">
                Apply Split (optional)
                {(actions.apply_split?.length ?? 0) > 0 && (
                  <span className={`ml-2 ${splitTotal === 100 ? "text-emerald-400" : "text-amber-400"}`}>
                    {splitTotal}% total
                  </span>
                )}
              </label>
              {participants.length > 0 && (
                <button type="button" onClick={addSplitEntry} className="text-xs text-indigo-400 hover:text-indigo-300">
                  + Add participant
                </button>
              )}
            </div>
            {(actions.apply_split || []).map((entry, i) => (
              <div key={i} className="flex gap-2 mb-2 items-center">
                <select
                  value={entry.participant_id}
                  onChange={(e) => updateSplitEntry(i, { participant_id: Number(e.target.value) })}
                  className="flex-1 bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                >
                  {participants.map((p) => (
                    <option key={p.id} value={p.id}>{p.name}</option>
                  ))}
                </select>
                <input
                  type="number"
                  min={0}
                  max={100}
                  value={entry.share_percent}
                  onChange={(e) => updateSplitEntry(i, { share_percent: Number(e.target.value) })}
                  className="w-20 bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                />
                <span className="text-xs text-zinc-500">%</span>
                <button
                  type="button"
                  onClick={() => removeSplitEntry(i)}
                  className="text-zinc-500 hover:text-red-400 text-lg leading-none px-1"
                >
                  ×
                </button>
              </div>
            ))}
            {participants.length === 0 && (
              <p className="text-xs text-zinc-600">No participants created yet.</p>
            )}
          </div>

          <div className="flex items-end gap-4">
            <div>
              <label className="block text-xs text-zinc-500 mb-1">Priority</label>
              <input
                type="number"
                value={priority}
                onChange={(e) => setPriority(Number(e.target.value))}
                className="w-24 bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-2 text-sm"
              />
            </div>
            <label className="flex items-center gap-2 text-sm text-zinc-400 cursor-pointer select-none pb-2">
              <input
                type="checkbox"
                checked={manualOnly}
                onChange={(e) => setManualOnly(e.target.checked)}
                className="accent-amber-500"
              />
              <span>
                Quick action only
                <span className="block text-xs text-zinc-600">
                  Never runs automatically — appears as a button on selected transactions
                </span>
              </span>
            </label>
            <button
              type="submit"
              disabled={isPending}
              className="px-6 py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg text-sm font-medium disabled:opacity-50"
            >
              {isPending ? "Saving..." : editingId !== null ? "Save Changes" : "Create Rule"}
            </button>
          </div>
        </form>
      )}

      {runs.length > 0 && (
        <div>
          <h3 className="text-sm font-medium text-zinc-400 mb-2">Apply History</h3>
          <div className="space-y-2">
            {runs.map((run) => (
              <div key={run.id} className={`rounded-lg border text-sm overflow-hidden ${run.reverted_at ? "bg-zinc-900/40 border-zinc-800 opacity-60" : "bg-zinc-900 border-zinc-700"}`}>
                <div className="flex items-center justify-between px-4 py-2.5">
                  <div className="flex items-center gap-3 flex-wrap">
                    <button
                      onClick={() => setExpandedRun(expandedRun === run.id ? null : run.id)}
                      className="text-zinc-500 hover:text-zinc-200 w-4 text-left"
                      title="Show what this run changed"
                    >
                      {expandedRun === run.id ? "▾" : "▸"}
                    </button>
                    <span className="text-zinc-300">{new Date(run.applied_at).toLocaleString()}</span>
                    {/* Which rule ran matters most: a merchant rename and a
                        50/50 split of everything look identical as counts. */}
                    <span className="text-zinc-200">
                      {run.rule_name ?? (run.source === "all" ? "All rules" : "Unknown rule")}
                    </span>
                    {run.source === "selection" && (
                      <span className="text-[10px] px-1.5 py-0.5 rounded bg-indigo-900/50 text-indigo-300">
                        selection
                      </span>
                    )}
                    <span className="text-zinc-500">{run.transactions_affected} transactions</span>
                    {run.split_from && (
                      <span className="text-zinc-600 text-xs">splits from {String(run.split_from).slice(0, 10)}</span>
                    )}
                  </div>
                  {run.reverted_at ? (
                    <span className="text-xs text-zinc-500">reverted {new Date(run.reverted_at).toLocaleString()}</span>
                  ) : (
                    <button
                      onClick={() => {
                        if (confirm("Revert this run? This will restore all affected transactions to their state before the rules were applied.")) {
                          revertRun.mutate(run.id);
                        }
                      }}
                      disabled={revertRun.isPending}
                      className="text-xs text-amber-400 hover:text-amber-300 disabled:opacity-50"
                    >
                      Revert
                    </button>
                  )}
                </div>
                {expandedRun === run.id && <RuleRunDetail runId={run.id} />}
              </div>
            ))}
          </div>
        </div>
      )}

      {isLoading ? (
        <p className="text-zinc-500 text-sm">Loading rules...</p>
      ) : rules.length === 0 ? (
        <p className="text-zinc-500 text-sm">No rules yet. Create one to auto-classify transactions.</p>
      ) : (
        <div className="space-y-3">
          {rules.map((rule) => {
            const conds = Array.isArray(rule.conditions) ? rule.conditions : [];
            const acts =
              rule.actions && typeof rule.actions === "object" ? (rule.actions as Actions) : {};
            return (
              <div key={rule.id} className="bg-zinc-900 border border-zinc-700 rounded-xl p-4">
                <div className="flex items-start justify-between gap-4">
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-3 mb-1">
                      <span className="font-medium text-sm">{rule.name}</span>
                      {rule.manual_only && (
                        <span className="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded bg-amber-900/40 text-amber-300 border border-amber-800/60">
                          Quick action
                        </span>
                      )}
                      <span className="text-xs text-zinc-500">priority: {rule.priority}</span>
                    </div>
                    <p className="text-xs text-zinc-400">
                      {rule.manual_only
                        ? "(fired by hand on selected transactions)"
                        : conds.length > 0
                        ? conds.map((c) => humanCondition(c, tagNames)).join(" AND ")
                        : "(matches all)"}
                    </p>
                    <p className="text-xs text-zinc-500 mt-1">{humanAction(acts, tagNames, participantNames)}</p>
                  </div>
                  <div className="flex items-center gap-3 shrink-0">
                    {/* Preview is safe for every rule, including quick actions:
                        it writes nothing, and the apply step takes the ids you
                        tick rather than re-running the conditions. */}
                    <button
                      onClick={() => setPreview({ id: rule.id, name: rule.name })}
                      className="text-xs text-indigo-400 hover:text-indigo-300"
                    >
                      Preview
                    </button>
                    {/* No blind Apply for quick actions: their conditions are
                        empty, so a bulk apply would hit every transaction. */}
                    {!rule.manual_only && (
                      <button
                        onClick={() => handleApply(rule.id)}
                        disabled={applyRules.isPending}
                        className="text-xs text-emerald-400 hover:text-emerald-300 disabled:opacity-50"
                      >
                        Apply
                      </button>
                    )}
                    <button
                      onClick={() => updateRule.mutate({ id: rule.id, enabled: !rule.enabled })}
                      className={`relative inline-flex h-5 w-9 rounded-full transition-colors ${
                        rule.enabled ? "bg-indigo-600" : "bg-zinc-700"
                      }`}
                    >
                      <span
                        className={`inline-block h-4 w-4 rounded-full bg-white shadow transform transition-transform mt-0.5 ${
                          rule.enabled ? "translate-x-4" : "translate-x-0.5"
                        }`}
                      />
                    </button>
                    <button
                      onClick={() => openEditForm({ id: rule.id, name: rule.name, conditions: conds as Condition[], actions: acts, priority: rule.priority, manual_only: rule.manual_only })}
                      className="text-zinc-400 hover:text-white text-sm"
                    >
                      Edit
                    </button>
                    <button
                      onClick={() => {
                        if (confirm("Delete this rule?")) deleteRule.mutate(rule.id);
                      }}
                      className="text-zinc-500 hover:text-red-400 text-sm"
                    >
                      Delete
                    </button>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}

      {preview && (
        <RulePreviewModal
          ruleId={preview.id}
          ruleName={preview.name}
          onClose={() => setPreview(null)}
        />
      )}
    </div>
  );
}
