import { NextRequest, NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { queryRaw } from "@/lib/db";
import { getTransactions } from "@/lib/queries";
import { evaluateCondition, type Condition, type Actions } from "@/lib/rules";
import { applyRuleActions, captureSnapshot } from "@/lib/rule-actions";


export async function GET(req: NextRequest) {
  const user = await getCurrentUser(req);
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 403 });

  const runs = await queryRaw<{
    id: number;
    applied_at: string;
    split_from: string | null;
    matched: number;
    transactions_affected: number;
    reverted_at: string | null;
    rule_id: number | null;
    rule_name: string | null;
    source: string | null;
  }>(
    `SELECT id, applied_at, split_from, matched, transactions_affected, reverted_at,
            rule_id, rule_name, source
     FROM rule_apply_runs WHERE owner_id = $1 ORDER BY applied_at DESC LIMIT 20`,
    [user.id]
  );

  return NextResponse.json(runs);
}

export async function POST(req: NextRequest) {
  const user = await getCurrentUser(req);
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 403 });

  const body = await req.json().catch(() => ({})) as { splitFrom?: string | null; ruleId?: number | null };
  const splitFrom = body.splitFrom || null;
  const ruleId = body.ruleId || null;

  // Manual-only rules ("quick actions") never take part in a condition-matched
  // run — their conditions are typically empty, so they would match every
  // transaction. They are fired from the transactions page against a selection.
  const rules = await queryRaw<{ id: number; name: string; conditions: unknown; actions: unknown }>(
    ruleId
      ? `SELECT id, name, conditions, actions FROM rules
         WHERE owner_id = $1 AND id = $2 AND manual_only = false`
      : `SELECT id, name, conditions, actions FROM rules
         WHERE owner_id = $1 AND enabled = true AND manual_only = false
         ORDER BY priority DESC`,
    ruleId ? [user.id, ruleId] : [user.id]
  );

  if (!rules.length) return NextResponse.json({ matched: 0, transactions_affected: 0 });

  const { data: transactions } = await getTransactions(user.id, { limit: 100000, offset: 0 });

  // --- Pre-pass: find all transactions that will match any rule ---
  const parsedRules = rules.map((r) => ({
    conditions: (typeof r.conditions === "string" ? JSON.parse(r.conditions) : r.conditions) as Condition[],
    actions: (typeof r.actions === "string" ? JSON.parse(r.actions) : r.actions) as Actions,
  }));

  const matchedIds = new Set<number>();
  for (const tx of transactions) {
    for (const { conditions } of parsedRules) {
      if (conditions.length === 0 || conditions.every((c) => evaluateCondition(c, tx))) {
        matchedIds.add(tx.id);
        break;
      }
    }
  }

  // --- Capture before-state for all matched transactions (batched) ---
  const snapshot = await captureSnapshot(Array.from(matchedIds));

  // --- Apply rules ---
  let matched = 0;
  const affectedIds = new Set<number>();

  for (const { conditions, actions } of parsedRules) {
    for (const tx of transactions) {
      const allMatch = conditions.length === 0 || conditions.every((c) => evaluateCondition(c, tx));
      if (!allMatch) continue;

      matched++;
      affectedIds.add(tx.id);

      // splitFrom holds the split back to transactions on/after that date;
      // category/merchant/tags still apply to everything matched.
      await applyRuleActions(tx.id, actions, {
        skipSplit: !!splitFrom && tx.transaction_date < splitFrom,
      });
    }
  }

  // --- Save run record ---
  const run = await queryRaw<{ id: number }>(
    `INSERT INTO rule_apply_runs (owner_id, split_from, matched, transactions_affected, snapshot,
       rule_id, rule_name, source)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`,
    [user.id, splitFrom, matched, affectedIds.size, JSON.stringify(snapshot),
     ruleId, ruleId ? (rules[0]?.name ?? null) : null, ruleId ? "rule" : "all"]
  );

  return NextResponse.json({ id: run[0].id, matched, transactions_affected: affectedIds.size });
}
