import { describe, test, expect } from "bun:test";
import { CatalogItemController } from "../../../src/controllers/CatalogItemController";
import { buildMockCatalogItem } from "../../setup";

describe("CatalogItemController", () => {
  // -------------------------------------------------------------------
  // Constructor
  // -------------------------------------------------------------------
  describe("constructor", () => {
    test("sets core identification fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.id).toBe("cat-1");
      expect(ctrl.cwCatalogId).toBe(500);
      expect(ctrl.identifier).toBe("USW-Pro-24");
    });

    test("sets name and description fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.name).toBe("UniFi Switch Pro 24");
      expect(ctrl.description).toBe("24-port managed switch");
      expect(ctrl.customerDescription).toBe("Enterprise switch");
      expect(ctrl.internalNotes).toBeNull();
    });

    test("sets category and subcategory fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.category).toBe("Technology");
      expect(ctrl.categoryCwId).toBe(18);
      expect(ctrl.subcategory).toBe("Network-Switch");
      expect(ctrl.subcategoryCwId).toBe(112);
    });

    test("sets manufacturer fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.manufacturer).toBe("Ubiquiti");
      expect(ctrl.manufactureCwId).toBe(248);
      expect(ctrl.partNumber).toBe("USW-Pro-24");
    });

    test("sets vendor fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.vendorName).toBe("Ubiquiti Inc");
      expect(ctrl.vendorSku).toBe("USW-Pro-24");
      expect(ctrl.vendorCwId).toBe(100);
    });

    test("sets financial fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.price).toBe(500.0);
      expect(ctrl.cost).toBe(360.0);
    });

    test("sets boolean flags", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.inactive).toBe(false);
      expect(ctrl.salesTaxable).toBe(true);
    });

    test("sets inventory fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.onHand).toBe(10);
    });

    test("sets timestamps", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.cwLastUpdated).toBeInstanceOf(Date);
      expect(ctrl.createdAt).toBeInstanceOf(Date);
      expect(ctrl.updatedAt).toBeInstanceOf(Date);
    });

    test("builds linked items recursively", () => {
      const linked = buildMockCatalogItem({
        id: "cat-2",
        name: "Linked Item",
        linkedItems: undefined,
      });
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({ linkedItems: [linked] }),
      );
      const items = ctrl.getLinkedItems();
      expect(items).toHaveLength(1);
      expect(items[0].id).toBe("cat-2");
      expect(items[0]).toBeInstanceOf(CatalogItemController);
    });

    test("defaults to empty linked items when undefined", () => {
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({ linkedItems: undefined }),
      );
      expect(ctrl.getLinkedItems()).toHaveLength(0);
    });

    test("handles null optional fields", () => {
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({
          description: null,
          customerDescription: null,
          identifier: null,
          category: null,
          categoryCwId: null,
          subcategory: null,
          subcategoryCwId: null,
          manufacturer: null,
          manufactureCwId: null,
          partNumber: null,
          vendorName: null,
          vendorSku: null,
          vendorCwId: null,
          cwLastUpdated: null,
        }),
      );
      expect(ctrl.description).toBeNull();
      expect(ctrl.customerDescription).toBeNull();
      expect(ctrl.identifier).toBeNull();
      expect(ctrl.category).toBeNull();
      expect(ctrl.manufacturer).toBeNull();
      expect(ctrl.cwLastUpdated).toBeNull();
    });
  });

  // -------------------------------------------------------------------
  // getLinkedItems
  // -------------------------------------------------------------------
  describe("getLinkedItems()", () => {
    test("returns empty array when no linked items", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      expect(ctrl.getLinkedItems()).toEqual([]);
    });

    test("returns array of CatalogItemController instances", () => {
      const linked1 = buildMockCatalogItem({ id: "cat-2", name: "Item 2" });
      const linked2 = buildMockCatalogItem({ id: "cat-3", name: "Item 3" });
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({ linkedItems: [linked1, linked2] }),
      );
      const items = ctrl.getLinkedItems();
      expect(items).toHaveLength(2);
      expect(items[0].name).toBe("Item 2");
      expect(items[1].name).toBe("Item 3");
    });
  });

  // -------------------------------------------------------------------
  // toJson
  // -------------------------------------------------------------------
  describe("toJson()", () => {
    test("returns all core fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      const json = ctrl.toJson();
      expect(json.id).toBe("cat-1");
      expect(json.cwCatalogId).toBe(500);
      expect(json.identifier).toBe("USW-Pro-24");
      expect(json.name).toBe("UniFi Switch Pro 24");
      expect(json.description).toBe("24-port managed switch");
      expect(json.customerDescription).toBe("Enterprise switch");
      expect(json.internalNotes).toBeNull();
    });

    test("returns classification fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      const json = ctrl.toJson();
      expect(json.category).toBe("Technology");
      expect(json.categoryCwId).toBe(18);
      expect(json.subcategory).toBe("Network-Switch");
      expect(json.subcategoryCwId).toBe(112);
      expect(json.manufacturer).toBe("Ubiquiti");
      expect(json.manufactureCwId).toBe(248);
      expect(json.partNumber).toBe("USW-Pro-24");
    });

    test("returns financial fields", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      const json = ctrl.toJson();
      expect(json.price).toBe(500.0);
      expect(json.cost).toBe(360.0);
    });

    test("returns boolean flags", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      const json = ctrl.toJson();
      expect(json.inactive).toBe(false);
      expect(json.salesTaxable).toBe(true);
    });

    test("returns timestamps", () => {
      const ctrl = new CatalogItemController(buildMockCatalogItem());
      const json = ctrl.toJson();
      expect(json.createdAt).toBeInstanceOf(Date);
      expect(json.updatedAt).toBeInstanceOf(Date);
      expect(json.cwLastUpdated).toBeInstanceOf(Date);
    });

    test("excludes linkedItems when includeLinkedItems not set", () => {
      const linked = buildMockCatalogItem({ id: "cat-2" });
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({ linkedItems: [linked] }),
      );
      const json = ctrl.toJson();
      expect(json.linkedItems).toBeUndefined();
    });

    test("includes linkedItems when includeLinkedItems is true", () => {
      const linked = buildMockCatalogItem({ id: "cat-2", name: "Linked" });
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({ linkedItems: [linked] }),
      );
      const json = ctrl.toJson({ includeLinkedItems: true });
      expect(json.linkedItems).toHaveLength(1);
      expect(json.linkedItems[0].id).toBe("cat-2");
      expect(json.linkedItems[0].name).toBe("Linked");
    });

    test("linked items toJson does not recursively include their linked items", () => {
      const linked = buildMockCatalogItem({ id: "cat-2" });
      const ctrl = new CatalogItemController(
        buildMockCatalogItem({ linkedItems: [linked] }),
      );
      const json = ctrl.toJson({ includeLinkedItems: true });
      // Nested linked items called without opts, so linkedItems is undefined
      expect(json.linkedItems[0].linkedItems).toBeUndefined();
    });
  });
});
