// SecurityPicker — unit tests (Issue #214).
//
// NOTE: this project has no jsdom / @testing-library harness (logged across
// the balance UI work). We test the picker's pure decision logic — the
// filter and the create-vs-pick rule — directly, the same way
// CategoryCombobox.test.ts tests `sortHierarchical`. DOM rendering is not
// exercised here.

import { describe, it, expect } from "vitest";
import { filterSecurities, decideCreateOption } from "./SecurityPicker";
import type { BalanceSecurity } from "../../shared/types";

function sec(
  id: number,
  symbol: string,
  name: string | null,
  asset_type: "stock" | "crypto" = "stock"
): BalanceSecurity {
  return {
    id,
    symbol,
    name,
    currency: "CAD",
    asset_type,
    created_at: "",
    updated_at: "",
  };
}

const catalogue: BalanceSecurity[] = [
  sec(1, "AAPL", "Apple Inc."),
  sec(2, "BTC", "Bitcoin", "crypto"),
  sec(3, "ETH", "Ethereum", "crypto"),
  sec(4, "MSFT", "Microsoft Corp."),
];

describe("filterSecurities", () => {
  it("returns the whole catalogue for an empty / whitespace query", () => {
    expect(filterSecurities(catalogue, "")).toHaveLength(4);
    expect(filterSecurities(catalogue, "   ")).toHaveLength(4);
  });

  it("matches on symbol, case-insensitively", () => {
    const r = filterSecurities(catalogue, "aapl");
    expect(r.map((s) => s.symbol)).toEqual(["AAPL"]);
  });

  it("matches on name, case-insensitively", () => {
    const r = filterSecurities(catalogue, "micro");
    expect(r.map((s) => s.symbol)).toEqual(["MSFT"]);
  });

  it("matches a partial substring across multiple rows", () => {
    // "et" hits ETH's symbol and nothing else here.
    const r = filterSecurities(catalogue, "eth");
    expect(r.map((s) => s.symbol)).toEqual(["ETH"]);
  });

  it("preserves the catalogue order of matches", () => {
    const r = filterSecurities(catalogue, "t"); // BTC, ETH, MSFT all contain 't'
    expect(r.map((s) => s.symbol)).toEqual(["BTC", "ETH", "MSFT"]);
  });

  it("returns [] when nothing matches", () => {
    expect(filterSecurities(catalogue, "ZZZZ")).toEqual([]);
  });
});

describe("decideCreateOption", () => {
  it("returns null for an empty / whitespace query (nothing to create)", () => {
    expect(decideCreateOption(catalogue, "")).toBeNull();
    expect(decideCreateOption(catalogue, "  ")).toBeNull();
  });

  it("offers a normalized (UPPER/TRIM) create for a brand-new symbol", () => {
    expect(decideCreateOption(catalogue, "  tsla ")).toEqual({ symbol: "TSLA" });
  });

  it("returns null when the normalized symbol already exists (exact match)", () => {
    // Same symbol, different casing/whitespace — already in the catalogue, so
    // there is nothing new to create.
    expect(decideCreateOption(catalogue, "aapl")).toBeNull();
    expect(decideCreateOption(catalogue, "  AAPL ")).toBeNull();
  });

  it("still offers create when the query only PARTIALLY matches an existing symbol", () => {
    // "AAP" is a prefix of AAPL but not an exact symbol — the user may want a
    // distinct ticker, so the create option is offered.
    expect(decideCreateOption(catalogue, "AAP")).toEqual({ symbol: "AAP" });
  });
});
