/**
 * invoices.service billing-entity integration tests.
 *
 * Covers:
 *  - createInvoice with billingEntity=client snapshots clientName from client.fullName
 *  - createInvoice with billingEntity=company snapshots clientName from company.name
 *  - createInvoice throws ValidationError when client does not exist
 *  - createInvoice throws ValidationError when company is in another tenant (cross-tenant)
 *  - createInvoice uses entity primary email + address when none provided
 *  - createInvoice allows caller to override billingEmail and billingAddress
 *
 * Uses dynamic imports (PR 8 pattern) so env is loaded before service modules
 * touch `db`.
 */
import { describe, it, expect, beforeAll } from 'vitest';

describe('invoices.service - billing entity', () => {
  let createInvoice: typeof import('@/lib/services/invoices').createInvoice;

  let makePort: typeof import('../helpers/factories').makePort;
  let makeClient: typeof import('../helpers/factories').makeClient;
  let makeCompany: typeof import('../helpers/factories').makeCompany;
  let makeAuditMeta: typeof import('../helpers/factories').makeAuditMeta;

  let db: typeof import('@/lib/db').db;
  let clientContacts: typeof import('@/lib/db/schema/clients').clientContacts;
  let clientAddresses: typeof import('@/lib/db/schema/clients').clientAddresses;
  let companyAddresses: typeof import('@/lib/db/schema/companies').companyAddresses;

  let ValidationError: typeof import('@/lib/errors').ValidationError;

  beforeAll(async () => {
    const svc = await import('@/lib/services/invoices');
    createInvoice = svc.createInvoice;

    const factories = await import('../helpers/factories');
    makePort = factories.makePort;
    makeClient = factories.makeClient;
    makeCompany = factories.makeCompany;
    makeAuditMeta = factories.makeAuditMeta;

    const dbMod = await import('@/lib/db');
    db = dbMod.db;

    const clientsSchema = await import('@/lib/db/schema/clients');
    clientContacts = clientsSchema.clientContacts;
    clientAddresses = clientsSchema.clientAddresses;

    const companiesSchema = await import('@/lib/db/schema/companies');
    companyAddresses = companiesSchema.companyAddresses;

    const errors = await import('@/lib/errors');
    ValidationError = errors.ValidationError;
  });

  it('creates an invoice with billingEntity=client and snapshots clientName from client.fullName', async () => {
    const port = await makePort();
    const client = await makeClient({
      portId: port.id,
      overrides: { fullName: 'Alice Admiral' },
    });

    const invoice = await createInvoice(
      port.id,
      {
        billingEntity: { type: 'client', id: client.id },
        dueDate: '2026-12-31',
        paymentTerms: 'net30',
        currency: 'USD',
        lineItems: [{ description: 'Dockage', quantity: 1, unitPrice: 500 }],
      },
      makeAuditMeta({ portId: port.id }),
    );

    expect(invoice.id).toBeTruthy();
    expect(invoice.billingEntityType).toBe('client');
    expect(invoice.billingEntityId).toBe(client.id);
    expect(invoice.clientName).toBe('Alice Admiral');
    expect(invoice.portId).toBe(port.id);
  });

  it('creates an invoice with billingEntity=company and snapshots clientName from company.name', async () => {
    const port = await makePort();
    const company = await makeCompany({
      portId: port.id,
      overrides: { name: 'Poseidon Holdings Ltd' },
    });

    const invoice = await createInvoice(
      port.id,
      {
        billingEntity: { type: 'company', id: company.id },
        dueDate: '2026-12-31',
        paymentTerms: 'net30',
        currency: 'USD',
        lineItems: [{ description: 'Marina services', quantity: 2, unitPrice: 1250 }],
      },
      makeAuditMeta({ portId: port.id }),
    );

    expect(invoice.billingEntityType).toBe('company');
    expect(invoice.billingEntityId).toBe(company.id);
    expect(invoice.clientName).toBe('Poseidon Holdings Ltd');
  });

  it('throws ValidationError when billing entity (client) does not exist', async () => {
    const port = await makePort();

    await expect(
      createInvoice(
        port.id,
        {
          billingEntity: { type: 'client', id: 'nonexistent-client-id' },
          dueDate: '2026-12-31',
          paymentTerms: 'net30',
          currency: 'USD',
          lineItems: [{ description: 'Dockage', quantity: 1, unitPrice: 100 }],
        },
        makeAuditMeta({ portId: port.id }),
      ),
    ).rejects.toBeInstanceOf(ValidationError);
  });

  it('throws ValidationError when billing entity (company) is in a different port', async () => {
    const portA = await makePort();
    const portB = await makePort();
    const companyInB = await makeCompany({ portId: portB.id });

    await expect(
      createInvoice(
        portA.id,
        {
          billingEntity: { type: 'company', id: companyInB.id },
          dueDate: '2026-12-31',
          paymentTerms: 'net30',
          currency: 'USD',
          lineItems: [{ description: 'Dockage', quantity: 1, unitPrice: 100 }],
        },
        makeAuditMeta({ portId: portA.id }),
      ),
    ).rejects.toBeInstanceOf(ValidationError);
  });

  it('uses entity primary email + address when none provided in the request', async () => {
    const port = await makePort();
    const client = await makeClient({
      portId: port.id,
      overrides: { fullName: 'Bob Bosun' },
    });

    // Add a primary email contact and a primary address
    await db.insert(clientContacts).values({
      clientId: client.id,
      channel: 'email',
      value: 'bob@example.com',
      isPrimary: true,
    });
    await db.insert(clientAddresses).values({
      clientId: client.id,
      portId: port.id,
      label: 'Primary',
      streetAddress: '1 Pier Road',
      city: 'Harbor City',
      subdivisionIso: 'US-CA',
      postalCode: '90000',
      countryIso: 'US',
      isPrimary: true,
    });

    const invoice = await createInvoice(
      port.id,
      {
        billingEntity: { type: 'client', id: client.id },
        dueDate: '2026-12-31',
        paymentTerms: 'net30',
        currency: 'USD',
        lineItems: [{ description: 'Dockage', quantity: 1, unitPrice: 100 }],
      },
      makeAuditMeta({ portId: port.id }),
    );

    expect(invoice.billingEmail).toBe('bob@example.com');
    // Address is rendered using ISO->name lookup (US-CA -> California, US -> United States).
    expect(invoice.billingAddress).toBe(
      '1 Pier Road, Harbor City, California, 90000, United States',
    );
  });

  it('allows caller to override billingEmail and billingAddress', async () => {
    const port = await makePort();
    const company = await makeCompany({
      portId: port.id,
      overrides: { name: 'Nautical Corp', billingEmail: 'billing@nautical.example' },
    });

    await db.insert(companyAddresses).values({
      companyId: company.id,
      portId: port.id,
      label: 'Primary',
      streetAddress: '2 Ocean Blvd',
      city: 'Portville',
      subdivisionIso: 'US-FL',
      postalCode: '33101',
      countryIso: 'US',
      isPrimary: true,
    });

    const invoice = await createInvoice(
      port.id,
      {
        billingEntity: { type: 'company', id: company.id },
        billingEmail: 'override@example.com',
        billingAddress: 'Custom address line',
        dueDate: '2026-12-31',
        paymentTerms: 'net30',
        currency: 'USD',
        lineItems: [{ description: 'Dockage', quantity: 1, unitPrice: 100 }],
      },
      makeAuditMeta({ portId: port.id }),
    );

    expect(invoice.billingEmail).toBe('override@example.com');
    expect(invoice.billingAddress).toBe('Custom address line');
    // clientName snapshot is still entity-derived (not overridable on create)
    expect(invoice.clientName).toBe('Nautical Corp');
  });
});
