import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import type { Principal } from '../src/lib/auth';
import {
  assertApiKeyActive,
  AuthError,
  hashApiKey,
  requireCapability,
} from '../src/lib/auth';
import {
  apiKeyCreationResponse,
  apiKeyCreateSchema,
  apiKeyMetadata,
  generateApiKey,
  resolveApiKeyTarget,
} from '../src/routes/api-keys';

const userId = '00000000-0000-0000-0000-000000000001';
const otherUserId = '00000000-0000-0000-0000-000000000002';

function principal(overrides: Partial<Principal> = {}): Principal {
  return {
    userId,
    email: 'seller@example.com',
    name: 'Seller',
    isPlatformAdmin: false,
    teams: [{ team: 'demand', role: 'member' }],
    via: 'jwt',
    scopes: ['read', 'write'],
    ...overrides,
  };
}

function storedKey(overrides: Record<string, unknown> = {}) {
  return {
    id: '00000000-0000-0000-0000-000000000010',
    userId,
    name: 'Codex',
    keyHash: 'stored-hash',
    keyPrefix: 'pig_abc123',
    scopes: ['read'],
    lastUsedAt: null,
    expiresAt: null,
    revokedAt: null,
    createdAt: new Date('2026-08-12T12:00:00.000Z'),
    ...overrides,
  } as Parameters<typeof apiKeyMetadata>[0];
}

describe('API key lifecycle decisions', () => {
  it('stores only a SHA-256 hash and returns plaintext only from creation', () => {
    const generated = generateApiKey();
    assert.match(generated.key, /^pig_[A-Za-z0-9_-]{43}$/);
    assert.equal(generated.keyHash, hashApiKey(generated.key));
    assert.equal(generated.keyHash.length, 64);
    assert.equal(generated.keyPrefix, generated.key.slice(0, 10));

    const row = storedKey({ keyHash: generated.keyHash, keyPrefix: generated.keyPrefix });
    const created = apiKeyCreationResponse(row, generated.key);
    const listed = apiKeyMetadata(row);
    assert.equal(created.key, generated.key);
    assert.equal('key' in listed, false);
    assert.equal('keyHash' in listed, false);
  });

  it('allows read-only or read/write keys, never write-only keys', () => {
    assert.deepEqual(apiKeyCreateSchema.parse({ name: 'Reader' }).scopes, ['read']);
    assert.deepEqual(
      apiKeyCreateSchema.parse({ name: 'Writer', scopes: ['read', 'write'] }).scopes,
      ['read', 'write'],
    );
    assert.equal(apiKeyCreateSchema.safeParse({ name: 'Writer', scopes: ['write'] }).success, false);
    assert.equal(
      apiKeyCreateSchema.safeParse({ name: 'Duplicate', scopes: ['read', 'read'] }).success,
      false,
    );

    assert.throws(
      () => requireCapability(principal({ via: 'api_key', scopes: ['read'] }), 'deal:write', 'demand'),
      (error: unknown) => error instanceof AuthError && error.code === 'insufficient_scope',
    );
    assert.doesNotThrow(() =>
      requireCapability(
        principal({ via: 'api_key', scopes: ['read', 'write'] }),
        'deal:write',
        'demand',
      ),
    );
  });

  it('rejects a revoked key immediately while leaving an active key usable', () => {
    const now = new Date('2026-08-12T12:00:00.000Z');
    assert.doesNotThrow(() => assertApiKeyActive({ revokedAt: null, expiresAt: null }, now));
    assert.throws(
      () => assertApiKeyActive({ revokedAt: new Date('2026-08-12T11:59:00.000Z'), expiresAt: null }, now),
      (error: unknown) => error instanceof AuthError && error.code === 'revoked_key',
    );
  });

  it('keeps self-service personal, with explicit platform-admin cross-user access', () => {
    assert.equal(resolveApiKeyTarget(principal()), userId);
    assert.throws(
      () => resolveApiKeyTarget(principal(), otherUserId),
      (error: unknown) => error instanceof AuthError && error.code === 'insufficient_permission',
    );
    assert.equal(
      resolveApiKeyTarget(principal({ isPlatformAdmin: true }), otherUserId),
      otherUserId,
    );
    assert.throws(
      () => resolveApiKeyTarget(principal({ via: 'api_key' })),
      (error: unknown) =>
        error instanceof AuthError && error.code === 'credential_management_forbidden',
    );
  });
});
