import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import type { Allocation, Database } from '@pig/db';
import type { Principal } from '../src/lib/auth';
import { executeMutation } from '../src/lib/mutation';
import { createAllocationMutationDefinition } from '../src/routes/capacity-writes';
import {
  findCapacityViolation,
  type CommitmentCapacity,
  type ReservationCapacity,
} from '../src/services/capacity-writes';

const HOUR = 3_600_000;
const START = new Date('2026-01-01T00:00:00.000Z');

function at(hour: number): Date {
  return new Date(START.getTime() + hour * HOUR);
}

function commitment(overrides: Partial<CommitmentCapacity> = {}): CommitmentCapacity {
  return {
    id: '10000000-0000-4000-8000-000000000001',
    gpuCount: 8,
    startsAt: at(0),
    endsAt: at(10),
    totalGpuHours: 80,
    shape: null,
    oversubscriptionPct: 0,
    terminatedAt: null,
    ...overrides,
  };
}

function reservation(overrides: Partial<ReservationCapacity> = {}): ReservationCapacity {
  return {
    gpuHours: 80,
    startsAt: at(0),
    endsAt: at(10),
    status: 'committed',
    holdExpiresAt: null,
    ...overrides,
  };
}

describe('allocation availability invariant', () => {
  it('permits only the oversubscription explicitly recorded on the commitment', () => {
    const existing = reservation();
    const extra = reservation({ gpuHours: 20 });

    assert.equal(
      findCapacityViolation(
        commitment({ totalGpuHours: 100, oversubscriptionPct: 25 }),
        [existing],
        extra,
        at(-1),
      ),
      null,
    );
    assert.equal(
      findCapacityViolation(commitment({ totalGpuHours: 100 }), [existing], extra, at(-1))?.code,
      'shape_capacity_exceeded',
    );
  });

  it('ignores expired holds but live holds still reserve capacity', () => {
    const expired = reservation({
      status: 'planned',
      holdExpiresAt: at(-1),
    });
    const live = reservation({
      status: 'planned',
      holdExpiresAt: at(1),
    });
    const requested = reservation();

    assert.equal(findCapacityViolation(commitment(), [expired], requested, at(0)), null);
    assert.equal(
      findCapacityViolation(commitment(), [live], requested, at(0))?.code,
      'total_capacity_exceeded',
    );
  });

  it('checks each authoritative shape interval instead of averaging the term', () => {
    const shaped = commitment({
      endsAt: at(20),
      totalGpuHours: 120,
      shape: {
        intervals: [at(0).toISOString(), at(10).toISOString(), at(20).toISOString()],
        quantities: [8, 4],
      },
    });
    const firstTranche = reservation({ gpuHours: 60 });
    const overlapsRamp = reservation({ gpuHours: 30 });
    const fitsLaterTranche = reservation({
      gpuHours: 40,
      startsAt: at(10),
      endsAt: at(20),
    });

    assert.equal(
      findCapacityViolation(shaped, [firstTranche], overlapsRamp, at(-1))?.code,
      'shape_capacity_exceeded',
    );
    assert.equal(findCapacityViolation(shaped, [firstTranche], fitsLaterTranche, at(-1)), null);
  });
});

const principal: Principal = {
  userId: '00000000-0000-4000-8000-000000000001',
  email: 'seller@example.com',
  name: 'Seller',
  isPlatformAdmin: false,
  teams: [{ team: 'demand', role: 'member' }],
  via: 'jwt',
  scopes: ['read', 'write'],
};

describe('allocation mutation transaction', () => {
  it('passes the mutation transaction through the capacity check and audit write', async () => {
    const events: string[] = [];
    const tx = {
      insert: () => ({
        values: async () => {
          events.push('activity');
        },
      }),
    };
    const db = {
      transaction: async (work: (transaction: unknown) => Promise<unknown>) => {
        events.push('begin');
        const result = await work(tx);
        events.push('commit');
        return result;
      },
    } as unknown as Database;

    const definition = createAllocationMutationDefinition((transaction) => {
      assert.equal(transaction, tx);
      return {
        createAllocation: async (input) => {
          events.push('lock-check-insert');
          return {
            allocation: {
              id: '30000000-0000-4000-8000-000000000003',
              capacityCommitmentId: input.capacityCommitmentId,
              demandDealId: input.demandDealId,
              gpuHours: String(input.gpuHours),
              pricePerGpuHourCents: input.pricePerGpuHourCents,
              status: input.status,
            } as Allocation,
            commitment: {
              id: input.capacityCommitmentId,
              name: 'Eight H100s',
            },
            deal: {
              id: input.demandDealId,
              accountId: '40000000-0000-4000-8000-000000000004',
            },
          };
        },
        createCommitment: async () => assert.fail('wrong mutation'),
        updateCommitment: async () => assert.fail('wrong mutation'),
        createHold: async () => assert.fail('wrong mutation'),
        releaseAllocation: async () => assert.fail('wrong mutation'),
      };
    });

    await executeMutation(
      db,
      principal,
      async () => ({
        capacityCommitmentId: '10000000-0000-4000-8000-000000000001',
        demandDealId: '20000000-0000-4000-8000-000000000002',
        gpuHours: 8,
        pricePerGpuHourCents: 225,
        startsAt: '2026-01-01T00:00:00.000Z',
        endsAt: '2026-01-01T01:00:00.000Z',
        status: 'committed',
      }),
      definition,
    );

    assert.deepEqual(events, ['begin', 'lock-check-insert', 'activity', 'commit']);
  });
});
