import { describe, test, expect } from "bun:test";
import { OpportunityController } from "../../../src/controllers/OpportunityController";
import { ActivityController } from "../../../src/controllers/ActivityController";
import { CompanyController } from "../../../src/controllers/CompanyController";
import {
  buildMockOpportunity,
  buildMockCompany,
  buildMockCWActivity,
} from "../../setup";

describe("OpportunityController", () => {
  // -------------------------------------------------------------------
  // Constructor
  // -------------------------------------------------------------------
  describe("constructor", () => {
    test("sets core identification fields", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.id).toBe("opp-1");
      expect(ctrl.cwOpportunityId).toBe(1001);
      expect(ctrl.name).toBe("Test Opportunity");
      expect(ctrl.notes).toBe("Some notes");
    });

    test("sets type, stage, status references", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.typeName).toBe("New Business");
      expect(ctrl.typeCwId).toBe(1);
      expect(ctrl.stageName).toBe("Proposal");
      expect(ctrl.stageCwId).toBe(2);
      expect(ctrl.statusName).toBe("Active");
      expect(ctrl.statusCwId).toBe(3);
    });

    test("sets priority, rating, source", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.priorityName).toBe("High");
      expect(ctrl.priorityCwId).toBe(4);
      expect(ctrl.ratingName).toBe("Hot");
      expect(ctrl.ratingCwId).toBe(5);
      expect(ctrl.source).toBe("Referral");
    });

    test("sets sales rep fields", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.primarySalesRepName).toBe("John");
      expect(ctrl.primarySalesRepIdentifier).toBe("jroberts");
      expect(ctrl.primarySalesRepCwId).toBe(10);
      expect(ctrl.secondarySalesRepName).toBeNull();
    });

    test("sets company/contact/site fields", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.companyCwId).toBe(123);
      expect(ctrl.companyName).toBe("Test Company");
      expect(ctrl.contactCwId).toBe(200);
      expect(ctrl.contactName).toBe("Jane Doe");
      expect(ctrl.siteCwId).toBe(300);
      expect(ctrl.siteName).toBe("Main Office");
    });

    test("sets financial and location fields", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.totalSalesTax).toBe(50.0);
      expect(ctrl.customerPO).toBe("PO-12345");
      expect(ctrl.locationName).toBe("HQ");
      expect(ctrl.departmentName).toBe("Sales");
    });

    test("sets date fields", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      expect(ctrl.expectedCloseDate).toBeInstanceOf(Date);
      expect(ctrl.pipelineChangeDate).toBeInstanceOf(Date);
      expect(ctrl.dateBecameLead).toBeInstanceOf(Date);
      expect(ctrl.closedDate).toBeNull();
      expect(ctrl.closedFlag).toBe(false);
    });

    test("accepts company controller via opts", () => {
      const company = new CompanyController(buildMockCompany());
      const ctrl = new OpportunityController(buildMockOpportunity(), {
        company,
      });
      const json = ctrl.toJson();
      // Company should be a full object, not just {id, name}
      expect(json.company.id).toBe("company-1");
      expect(json.company.name).toBe("Test Company");
    });

    test("accepts activities via opts", () => {
      const activities = [new ActivityController(buildMockCWActivity())];
      const ctrl = new OpportunityController(buildMockOpportunity(), {
        activities,
      });
      const json = ctrl.toJson();
      expect(json.activities).toHaveLength(1);
      expect(json.activities[0].cwActivityId).toBe(5001);
    });

    test("accepts customFields via opts", () => {
      const customFields = [
        {
          id: 1,
          caption: "Custom1",
          type: "Text",
          entryMethod: "EntryField",
          numberOfDecimals: 0,
          value: "test",
        },
      ];
      const ctrl = new OpportunityController(buildMockOpportunity(), {
        customFields,
      });
      const json = ctrl.toJson();
      expect(json.customFields).toHaveLength(1);
    });

    test("has empty activities/customFields without opts", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.activities).toEqual([]);
      expect(json.customFields).toEqual([]);
    });
  });

  // -------------------------------------------------------------------
  // mapCwToDb (static)
  // -------------------------------------------------------------------
  describe("mapCwToDb()", () => {
    const cwOpportunity = {
      id: 1001,
      name: "CW Opp",
      notes: "CW notes",
      type: { id: 1, name: "New Business" },
      stage: { id: 2, name: "Proposal" },
      status: { id: 3, name: "Active" },
      priority: { id: 4, name: "High" },
      rating: null,
      source: "Web",
      campaign: null,
      primarySalesRep: { id: 10, identifier: "jroberts", name: "John" },
      secondarySalesRep: null,
      company: { id: 123, identifier: "TestCo", name: "Test Co" },
      contact: { id: 200, name: "Jane" },
      site: { id: 300, name: "Main" },
      customerPO: "PO-1",
      totalSalesTax: 25.5,
      location: { id: 400, name: "HQ" },
      department: { id: 500, name: "Sales" },
      expectedCloseDate: "2026-04-01T00:00:00Z",
      pipelineChangeDate: "2026-02-15T00:00:00Z",
      dateBecameLead: "2026-01-01T00:00:00Z",
      closedDate: null,
      closedFlag: false,
      closedBy: null,
      customFields: [],
      _info: { lastUpdated: "2026-02-28T12:00:00Z" },
    } as any;

    test("maps name and notes", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.name).toBe("CW Opp");
      expect(result.notes).toBe("CW notes");
    });

    test("maps type, stage, status references", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.typeId).toBe(1);
      expect(result.stageId).toBe(2);
      expect(result.statusId).toBe(3);
    });

    test("maps null references to null", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.ratingName).toBeNull();
      expect(result.ratingCwId).toBeNull();
      expect(result.campaignName).toBeNull();
    });

    test("maps sales rep fields", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.primarySalesRepName).toBe("John");
      expect(result.primarySalesRepIdentifier).toBe("jroberts");
      expect(result.secondarySalesRepName).toBeNull();
    });

    test("maps dates to Date objects", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.expectedCloseDate).toBeInstanceOf(Date);
      expect(result.closedDate).toBeNull();
    });

    test("maps closedFlag", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.closedFlag).toBe(false);
    });

    test("maps cwLastUpdated from _info", () => {
      const result = OpportunityController.mapCwToDb(cwOpportunity);
      expect(result.cwLastUpdated).toBeInstanceOf(Date);
    });
  });

  // -------------------------------------------------------------------
  // toJson
  // -------------------------------------------------------------------
  describe("toJson()", () => {
    test("returns core fields", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.id).toBe("opp-1");
      expect(json.cwOpportunityId).toBe(1001);
      expect(json.name).toBe("Test Opportunity");
    });

    test("formats type as reference object", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.type).toEqual({ id: 1, name: "New Business" });
    });

    test("formats stage as reference object", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.stage).toEqual({ id: 2, name: "Proposal" });
    });

    test("formats status as reference object", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.status).toEqual({ id: 3, name: "Active" });
    });

    test("formats primarySalesRep with identifier", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.primarySalesRep).toEqual({
        id: 10,
        identifier: "jroberts",
        name: "John",
      });
    });

    test("secondarySalesRep is null when not set", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.secondarySalesRep).toBeNull();
    });

    test("contact formats as reference object", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.contact).toEqual({ id: 200, name: "Jane Doe" });
    });

    test("company falls back to CW reference when no controller", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.company).toEqual({ id: 123, name: "Test Company" });
    });

    test("includes financial data", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.totalSalesTax).toBe(50.0);
      expect(json.customerPO).toBe("PO-12345");
    });

    test("includes dates", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.expectedCloseDate).toBeInstanceOf(Date);
      expect(json.closedFlag).toBe(false);
    });

    test("includes timestamps", () => {
      const ctrl = new OpportunityController(buildMockOpportunity());
      const json = ctrl.toJson();
      expect(json.createdAt).toBeInstanceOf(Date);
      expect(json.updatedAt).toBeInstanceOf(Date);
    });
  });
});
