import { describe, it, expect } from "vitest";
import { evaluateCondition, type Condition, type TxFields } from "@/lib/rules";

function tx(overrides: Partial<TxFields> = {}): TxFields {
  return {
    effective_category: "groceries",
    effective_merchant: "Coles",
    description: "COLES WYNDHAM VALE",
    bank_name: "ANZ",
    amount: 42.5,
    transaction_type: "debit",
    tags: [],
    ...overrides,
  };
}

function cond(field: Condition["field"], operator: Condition["operator"], value: string): Condition {
  return { field, operator, value };
}

// ── String fields ─────────────────────────────────────────────────────────────

describe("merchant_normalized", () => {
  it("contains — matches substring", () => {
    expect(evaluateCondition(cond("merchant_normalized", "contains", "coles"), tx())).toBe(true);
  });
  it("contains — case-insensitive", () => {
    expect(evaluateCondition(cond("merchant_normalized", "contains", "COLES"), tx())).toBe(true);
  });
  it("contains — no match", () => {
    expect(evaluateCondition(cond("merchant_normalized", "contains", "woolworths"), tx())).toBe(false);
  });
  it("equals — exact match (case-insensitive)", () => {
    expect(evaluateCondition(cond("merchant_normalized", "equals", "coles"), tx())).toBe(true);
  });
  it("equals — no match", () => {
    expect(evaluateCondition(cond("merchant_normalized", "equals", "cole"), tx())).toBe(false);
  });
  it("starts_with — matches prefix", () => {
    expect(evaluateCondition(cond("merchant_normalized", "starts_with", "col"), tx())).toBe(true);
  });
  it("starts_with — no match", () => {
    expect(evaluateCondition(cond("merchant_normalized", "starts_with", "oles"), tx())).toBe(false);
  });
  it("not_equals — different value", () => {
    expect(evaluateCondition(cond("merchant_normalized", "not_equals", "woolworths"), tx())).toBe(true);
  });
  it("not_equals — same value", () => {
    expect(evaluateCondition(cond("merchant_normalized", "not_equals", "coles"), tx())).toBe(false);
  });
  it("empty merchant falls back to empty string", () => {
    expect(evaluateCondition(cond("merchant_normalized", "contains", "coles"), tx({ effective_merchant: "" }))).toBe(false);
  });
});

describe("description", () => {
  it("contains — matches", () => {
    expect(evaluateCondition(cond("description", "contains", "wyndham"), tx())).toBe(true);
  });
  it("equals — exact (case-insensitive)", () => {
    expect(evaluateCondition(cond("description", "equals", "coles wyndham vale"), tx())).toBe(true);
  });
  it("starts_with", () => {
    expect(evaluateCondition(cond("description", "starts_with", "coles"), tx())).toBe(true);
  });
});

describe("category", () => {
  it("equals category", () => {
    expect(evaluateCondition(cond("category", "equals", "groceries"), tx())).toBe(true);
  });
  it("not_equals different category", () => {
    expect(evaluateCondition(cond("category", "not_equals", "dining"), tx())).toBe(true);
  });
  it("contains partial", () => {
    expect(evaluateCondition(cond("category", "contains", "grocer"), tx())).toBe(true);
  });
});

describe("bank_name", () => {
  it("equals bank", () => {
    expect(evaluateCondition(cond("bank_name", "equals", "anz"), tx())).toBe(true);
  });
  it("not_equals different bank", () => {
    expect(evaluateCondition(cond("bank_name", "not_equals", "nab"), tx())).toBe(true);
  });
});

describe("transaction_type", () => {
  it("equals debit", () => {
    expect(evaluateCondition(cond("transaction_type", "equals", "debit"), tx())).toBe(true);
  });
  it("not_equals credit", () => {
    expect(evaluateCondition(cond("transaction_type", "not_equals", "credit"), tx())).toBe(true);
  });
  it("equals credit — no match on debit tx", () => {
    expect(evaluateCondition(cond("transaction_type", "equals", "credit"), tx())).toBe(false);
  });
});

// ── Amount field ──────────────────────────────────────────────────────────────

describe("amount", () => {
  it("equals exact amount", () => {
    expect(evaluateCondition(cond("amount", "equals", "42.5"), tx())).toBe(true);
  });
  it("equals wrong amount", () => {
    expect(evaluateCondition(cond("amount", "equals", "42"), tx())).toBe(false);
  });
  it("not_equals different amount", () => {
    expect(evaluateCondition(cond("amount", "not_equals", "100"), tx())).toBe(true);
  });
  it("gt — amount is greater", () => {
    expect(evaluateCondition(cond("amount", "gt", "40"), tx())).toBe(true);
  });
  it("gt — amount is equal (not strictly greater)", () => {
    expect(evaluateCondition(cond("amount", "gt", "42.5"), tx())).toBe(false);
  });
  it("gt — amount is less", () => {
    expect(evaluateCondition(cond("amount", "gt", "50"), tx())).toBe(false);
  });
  it("lt — amount is less", () => {
    expect(evaluateCondition(cond("amount", "lt", "50"), tx())).toBe(true);
  });
  it("lt — amount is equal (not strictly less)", () => {
    expect(evaluateCondition(cond("amount", "lt", "42.5"), tx())).toBe(false);
  });
  it("lt — amount is greater", () => {
    expect(evaluateCondition(cond("amount", "lt", "40"), tx())).toBe(false);
  });
  it("unsupported operator (contains) returns false", () => {
    expect(evaluateCondition(cond("amount", "contains", "42"), tx())).toBe(false);
  });
});

// ── Tag field ────────────────────────────────────────────────────────────────

describe("tag", () => {
  it("equals — tag present", () => {
    expect(evaluateCondition(cond("tag", "equals", "5"), tx({ tags: [{ id: 5 }] }))).toBe(true);
  });
  it("equals — tag absent", () => {
    expect(evaluateCondition(cond("tag", "equals", "5"), tx({ tags: [] }))).toBe(false);
  });
  it("equals — different tag", () => {
    expect(evaluateCondition(cond("tag", "equals", "5"), tx({ tags: [{ id: 7 }] }))).toBe(false);
  });
  it("not_equals — tag absent", () => {
    expect(evaluateCondition(cond("tag", "not_equals", "5"), tx({ tags: [] }))).toBe(true);
  });
  it("not_equals — tag present", () => {
    expect(evaluateCondition(cond("tag", "not_equals", "5"), tx({ tags: [{ id: 5 }] }))).toBe(false);
  });
  it("matches one of multiple tags", () => {
    expect(evaluateCondition(cond("tag", "equals", "3"), tx({ tags: [{ id: 1 }, { id: 3 }] }))).toBe(true);
  });
});

// ── Unknown field ─────────────────────────────────────────────────────────────

describe("unknown field", () => {
  it("returns false for unrecognised field", () => {
    // @ts-expect-error intentional invalid field for regression guard
    expect(evaluateCondition({ field: "nonexistent", operator: "equals", value: "x" }, tx())).toBe(false);
  });
});
