/**
 * Schema constraint integration tests.
 *
 * Verifies DB-level enforcement of:
 *  - Partial unique index idx_yoh_active (one active ownership row per yacht)
 *  - Partial unique index idx_br_active (one active reservation per berth)
 *  - Non-active reservations on the same berth are permitted
 *  - Case-insensitive company name uniqueness within a port
 *  - Same company name allowed across different ports
 */
import { describe, it, expect, beforeAll } from 'vitest';

import { db } from '@/lib/db';
import { yachtOwnershipHistory } from '@/lib/db/schema/yachts';
import { berthTenancies } from '@/lib/db/schema/tenancies';
import { companies } from '@/lib/db/schema/companies';
import { makeBerth, makeClient, makeCompany, makePort, makeYacht } from '../helpers/factories';

// ─── DB availability ─────────────────────────────────────────────────────────

let dbAvailable = false;

beforeAll(async () => {
  try {
    await db.execute(`SELECT 1`);
    dbAvailable = true;
  } catch (err) {
    console.warn(
      '[schema-constraints] DATABASE_URL not reachable - skipping integration tests',
      err,
    );
  }
});

function itDb(name: string, fn: () => Promise<void>) {
  it(name, async () => {
    if (!dbAvailable) return;
    await fn();
  });
}

// ─── Tests ───────────────────────────────────────────────────────────────────

describe('schema constraints', () => {
  itDb(
    'rejects a second active ownership row per yacht (partial unique idx_yoh_active)',
    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,
      });
      // makeYacht already inserted one active (end_date IS NULL) ownership row.

      await expect(
        db.insert(yachtOwnershipHistory).values({
          yachtId: yacht.id,
          ownerType: 'client',
          ownerId: clientB.id,
          startDate: new Date(),
          endDate: null, // another open row - should violate partial unique
          createdBy: 'test',
        }),
      ).rejects.toMatchObject({
        cause: expect.objectContaining({ code: '23505' }),
      });
    },
  );

  itDb('rejects a second active reservation per berth (partial unique idx_br_active)', async () => {
    const port = await makePort();
    const clientA = await makeClient({ portId: port.id });
    const clientB = await makeClient({ portId: port.id });
    const yachtA = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientA.id,
    });
    const yachtB = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientB.id,
    });
    const berth = await makeBerth({ portId: port.id });

    await db.insert(berthTenancies).values({
      berthId: berth.id,
      portId: port.id,
      clientId: clientA.id,
      yachtId: yachtA.id,
      status: 'active',
      startDate: new Date(),
      createdBy: 'test',
    });

    await expect(
      db.insert(berthTenancies).values({
        berthId: berth.id,
        portId: port.id,
        clientId: clientB.id,
        yachtId: yachtB.id,
        status: 'active',
        startDate: new Date(),
        createdBy: 'test',
      }),
    ).rejects.toMatchObject({
      cause: expect.objectContaining({ code: '23505' }),
    });
  });

  itDb(
    'allows multiple non-active reservations on the same berth (partial index ignores non-active)',
    async () => {
      const port = await makePort();
      const clientA = await makeClient({ portId: port.id });
      const yachtA = await makeYacht({
        portId: port.id,
        ownerType: 'client',
        ownerId: clientA.id,
      });
      const berth = await makeBerth({ portId: port.id });

      // Two ended reservations on same berth - both should succeed
      // (partial index only constrains status='active').
      await expect(
        db.insert(berthTenancies).values([
          {
            berthId: berth.id,
            portId: port.id,
            clientId: clientA.id,
            yachtId: yachtA.id,
            status: 'ended',
            startDate: new Date('2024-01-01'),
            endDate: new Date('2024-06-30'),
            createdBy: 'test',
          },
          {
            berthId: berth.id,
            portId: port.id,
            clientId: clientA.id,
            yachtId: yachtA.id,
            status: 'ended',
            startDate: new Date('2024-07-01'),
            endDate: new Date('2024-12-31'),
            createdBy: 'test',
          },
        ]),
      ).resolves.toBeDefined();
    },
  );

  itDb('enforces case-insensitive company name uniqueness per port', async () => {
    const port = await makePort();
    await makeCompany({ portId: port.id, overrides: { name: 'Aegean Holdings' } });

    await expect(
      db.insert(companies).values({ portId: port.id, name: 'AEGEAN HOLDINGS' }),
    ).rejects.toMatchObject({
      cause: expect.objectContaining({ code: '23505' }),
    });
  });

  itDb('allows same-name companies in different ports', async () => {
    const portA = await makePort();
    const portB = await makePort();
    await makeCompany({ portId: portA.id, overrides: { name: 'Aegean Holdings' } });

    await expect(
      db.insert(companies).values({ portId: portB.id, name: 'Aegean Holdings' }),
    ).resolves.toBeDefined();
  });
});
