/**
 * interests.service yacht-ownership validation integration tests.
 *
 * Covers:
 *  - createInterest with yachtId succeeds when yacht is owned by the client
 *  - createInterest with yachtId rejects when yacht belongs to a different client
 *  - createInterest with yachtId succeeds when client is member of owning company
 *  - createInterest without yachtId succeeds (stage=open is allowed)
 *  - changeInterestStage rejects moving out of "open" when yachtId is null
 *  - changeInterestStage succeeds when yachtId is set
 *  - updateInterest validates yacht ownership when changing yachtId
 *
 * Uses dynamic imports (PR 8 pattern) so env is loaded before service modules
 * touch `db`.
 */
import { describe, it, expect, beforeAll } from 'vitest';

describe('interests.service - yacht ownership validation', () => {
  let createInterest: typeof import('@/lib/services/interests.service').createInterest;

  let updateInterest: typeof import('@/lib/services/interests.service').updateInterest;

  let changeInterestStage: typeof import('@/lib/services/interests.service').changeInterestStage;

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

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

  let makeYacht: typeof import('../helpers/factories').makeYacht;

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

  let makeMembership: typeof import('../helpers/factories').makeMembership;

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

  beforeAll(async () => {
    const svc = await import('@/lib/services/interests.service');
    createInterest = svc.createInterest;
    updateInterest = svc.updateInterest;
    changeInterestStage = svc.changeInterestStage;

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

  it('createInterest with yachtId succeeds when yacht is owned by the client', async () => {
    const port = await makePort();
    const client = await makeClient({ portId: port.id });
    const yacht = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: client.id,
    });

    const interest = await createInterest(
      port.id,
      {
        clientId: client.id,
        yachtId: yacht.id,
        pipelineStage: 'enquiry',
        tagIds: [],
        reminderEnabled: false,
      },
      makeAuditMeta({ portId: port.id }),
    );
    expect(interest.yachtId).toBe(yacht.id);
    expect(interest.clientId).toBe(client.id);
  });

  it('createInterest with yachtId rejects when yacht belongs to a different client', async () => {
    const port = await makePort();
    const clientA = await makeClient({ portId: port.id });
    const clientB = await makeClient({ portId: port.id });
    const yacht = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientA.id,
    });

    await expect(
      createInterest(
        port.id,
        {
          clientId: clientB.id,
          yachtId: yacht.id,
          pipelineStage: 'enquiry',
          tagIds: [],
          reminderEnabled: false,
        },
        makeAuditMeta({ portId: port.id }),
      ),
    ).rejects.toThrow(/yacht does not belong to this client/);
  });

  it('createInterest with yachtId succeeds when client is member of owning company', async () => {
    const port = await makePort();
    const client = await makeClient({ portId: port.id });
    const company = await makeCompany({ portId: port.id });
    await makeMembership({
      companyId: company.id,
      clientId: client.id,
      role: 'director',
      endDate: null,
    });
    const yacht = await makeYacht({
      portId: port.id,
      ownerType: 'company',
      ownerId: company.id,
    });

    const interest = await createInterest(
      port.id,
      {
        clientId: client.id,
        yachtId: yacht.id,
        pipelineStage: 'enquiry',
        tagIds: [],
        reminderEnabled: false,
      },
      makeAuditMeta({ portId: port.id }),
    );
    expect(interest.yachtId).toBe(yacht.id);
  });

  it('createInterest without yachtId succeeds (stage=open is allowed)', async () => {
    const port = await makePort();
    const client = await makeClient({ portId: port.id });

    const interest = await createInterest(
      port.id,
      { clientId: client.id, pipelineStage: 'enquiry', tagIds: [], reminderEnabled: false },
      makeAuditMeta({ portId: port.id }),
    );
    expect(interest.yachtId).toBeNull();
    expect(interest.pipelineStage).toBe('enquiry');
  });

  it('changeInterestStage rejects moving out of "enquiry" when yachtId is null', async () => {
    const port = await makePort();
    const client = await makeClient({ portId: port.id });
    const interest = await createInterest(
      port.id,
      { clientId: client.id, pipelineStage: 'enquiry', tagIds: [], reminderEnabled: false },
      makeAuditMeta({ portId: port.id }),
    );

    await expect(
      changeInterestStage(
        interest.id,
        port.id,
        { pipelineStage: 'qualified' },
        makeAuditMeta({ portId: port.id }),
      ),
    ).rejects.toThrow(/yacht must be linked before leaving the Enquiry stage/i);
  });

  it('changeInterestStage succeeds when yachtId is set', async () => {
    const port = await makePort();
    const client = await makeClient({ portId: port.id });
    const yacht = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: client.id,
    });
    const interest = await createInterest(
      port.id,
      {
        clientId: client.id,
        yachtId: yacht.id,
        pipelineStage: 'enquiry',
        tagIds: [],
        reminderEnabled: false,
      },
      makeAuditMeta({ portId: port.id }),
    );

    const updated = await changeInterestStage(
      interest.id,
      port.id,
      { pipelineStage: 'qualified' },
      makeAuditMeta({ portId: port.id }),
    );
    // After A19: changeInterestStage returns the sentinel STAGE_NOOP only
    // when target === current. Here the stage actually changes, so the
    // result is the updated row.
    if (typeof updated === 'symbol') throw new Error('unexpected no-op');
    expect(updated.pipelineStage).toBe('qualified');
  });

  it('updateInterest validates yacht ownership when changing yachtId', async () => {
    const port = await makePort();
    const clientA = await makeClient({ portId: port.id });
    const clientB = await makeClient({ portId: port.id });
    // Interest is owned by clientA; yacht belongs to clientB.
    const interest = await createInterest(
      port.id,
      { clientId: clientA.id, pipelineStage: 'enquiry', tagIds: [], reminderEnabled: false },
      makeAuditMeta({ portId: port.id }),
    );
    const yachtOfB = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientB.id,
    });

    await expect(
      updateInterest(
        interest.id,
        port.id,
        { yachtId: yachtOfB.id },
        makeAuditMeta({ portId: port.id }),
      ),
    ).rejects.toThrow(/yacht does not belong to this client/);

    // ... and succeeds when swapping in a yacht that clientA actually owns.
    const yachtOfA = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientA.id,
    });
    const updated = await updateInterest(
      interest.id,
      port.id,
      { yachtId: yachtOfA.id },
      makeAuditMeta({ portId: port.id }),
    );
    expect(updated.yachtId).toBe(yachtOfA.id);
  });
});
