import { jest } from "@jest/globals";
import { setupTestDb, cleanUpTestDb } from "../helpers/dbSetup.js";
import db from "../../src/models/index.js";
import CustomerController from "../../src/controllers/customerController.js";
import YouverifyService from "../../src/thirdParties/youVerifyService.js";
import ServiceGuard from "../../src/utils/serviceGuard.js";

describe("Customer KYC Verification & Service Guard (Unit Tests)", () => {
  let merchant;
  let customer;
  let verifyBvnSpy;
  let verifyNinSpy;

  // KYC Stages & Services variables
  let bvnStage;
  let ninStage;
  let virtualAccountService;
  let cardService;

  beforeAll(async () => {
    await setupTestDb();

    merchant = await db.Merchant.create({
      businessName: "Test Merchant",
      email: "testmerchant@airpero.com",
      password: "password123",
    });

    // Seed KYC Stages
    bvnStage = await db.KycStage.create({
      name: "BVN Stage",
      code: "bvn_stage",
      sequence: 1,
      description: "Stage 1: BVN Verification",
    });

    ninStage = await db.KycStage.create({
      name: "NIN Stage",
      code: "nin_stage",
      sequence: 2,
      description: "Stage 2: NIN ID Verification",
    });

    // Seed System Services
    virtualAccountService = await db.SystemService.create({
      name: "NGN Virtual Account Service",
      code: "ngn_virtual_account_service",
      currency: "NGN",
      type: "currency_based",
      isActive: true,
      canSend: true,
      canReceive: true,
    });

    cardService = await db.SystemService.create({
      name: "Card Service",
      code: "card_service",
      currency: null,
      type: "global_based",
      isActive: true,
      canSend: true,
      canReceive: true,
    });

    // Map virtual account service to bvn_stage
    await db.KycStageService.create({
      stageId: bvnStage.id,
      serviceId: virtualAccountService.id,
      isActive: true,
    });

    // Map card service to nin_stage
    await db.KycStageService.create({
      stageId: ninStage.id,
      serviceId: cardService.id,
      isActive: true,
    });
  });

  afterAll(async () => {
    await cleanUpTestDb();
  });

  beforeEach(async () => {
    verifyBvnSpy = jest.spyOn(YouverifyService, "verifyBvn");
    verifyNinSpy = jest.spyOn(YouverifyService, "verifyNin");

    // Create a fresh customer for each test
    customer = await db.Customer.create({
      merchantId: merchant.id,
      fullName: "Dev Sammy",
      email: `customer_${Date.now()}@test.com`,
      phoneNumber: "08012345678",
    });
  });

  afterEach(() => {
    if (verifyBvnSpy) verifyBvnSpy.mockRestore();
    if (verifyNinSpy) verifyNinSpy.mockRestore();
  });

  describe("Stage 1: BVN Verification", () => {
    it("should successfully verify customer details on Stage 1 (BVN)", async () => {
      verifyBvnSpy.mockResolvedValue({
        success: true,
        data: {
          firstName: "Dev",
          lastName: "Sammy",
        },
      });

      const req = {
        params: { customerId: customer.id },
        body: { bvn: "22222222222" },
        merchant: { id: merchant.id },
      };

      const res = {
        status: jest.fn().mockReturnThis(),
        json: jest.fn(),
      };

      await CustomerController.verifyCustomerBvn(req, res);

      expect(verifyBvnSpy).toHaveBeenCalledWith({ id: "22222222222" });
      expect(res.status).toHaveBeenCalledWith(200);

      const updatedCustomer = await db.Customer.findByPk(customer.id);
      expect(updatedCustomer.bvnStatus).toBe("verified");
    });
  });

  describe("Service Guard Gating Tests", () => {
    it("should restrict access to ngn_virtual_account_service if Stage 1 (BVN) is not verified", async () => {
      // Customer has bvnStatus = 'none'
      await expect(
        ServiceGuard.checkAccess(customer.id, "ngn_virtual_account_service")
      ).rejects.toThrow("Access Denied: Customer must complete Stage 1 (BVN Verification)");
    });

    it("should allow access to ngn_virtual_account_service if Stage 1 (BVN) is verified", async () => {
      await customer.update({ bvnStatus: "verified" });
      
      const hasAccess = await ServiceGuard.checkAccess(customer.id, "ngn_virtual_account_service");
      expect(hasAccess).toBe(true);
    });

    it("should restrict access to card_service if Stage 2 (NIN) is not verified", async () => {
      await customer.update({ bvnStatus: "verified", idStatus: "none" });

      await expect(
        ServiceGuard.checkAccess(customer.id, "card_service")
      ).rejects.toThrow("Access Denied: Customer must complete Stage 2 (NIN Verification)");
    });

    it("should allow access to card_service if Stage 2 (NIN) is verified", async () => {
      await customer.update({ bvnStatus: "verified", idStatus: "verified" });

      const hasAccess = await ServiceGuard.checkAccess(customer.id, "card_service");
      expect(hasAccess).toBe(true);
    });

    it("should block access to a service if it is globally deactivated, even if customer is verified", async () => {
      await customer.update({ bvnStatus: "verified" });
      
      // Globally deactivate service
      await virtualAccountService.update({ isActive: false });

      await expect(
        ServiceGuard.checkAccess(customer.id, "ngn_virtual_account_service")
      ).rejects.toThrow("currently deactivated globally");

      // Re-enable for subsequent tests
      await virtualAccountService.update({ isActive: true });
    });
  });
});
