/**
 * Pure-resolver tests for buildUsageIdentity().
 *
 * The resolver maps gateway-internal auth state to the four telemetry identity
 * fields (auth_kind, principal_id, customer_id, tier). It is intentionally
 * pure — no JWT verification, no key hashing of secrets, no I/O — so the
 * branch matrix is trivially testable here.
 */

import { describe, expect, test } from 'vitest';

import { buildUsageIdentity, type UsageIdentityInput } from '../_shared/usage-identity';

function baseInput(overrides: Partial<UsageIdentityInput> = {}): UsageIdentityInput {
  return {
    sessionUserId: null,
    isUserApiKey: false,
    enterpriseApiKey: null,
    widgetKey: null,
    clerkOrgId: null,
    userApiKeyCustomerRef: null,
    tier: null,
    planKey: null,
    ...overrides,
  };
}

describe('buildUsageIdentity — plan_key attribution (#4572)', () => {
  test('user_api_key carries the resolved planKey', () => {
    const ident = buildUsageIdentity(baseInput({
      isUserApiKey: true,
      sessionUserId: 'u',
      tier: 2,
      planKey: 'api_business',
    }));
    expect(ident.plan_key).toBe('api_business');
  });

  test('enterprise_api_key defaults plan_key to "enterprise" when none supplied', () => {
    const ident = buildUsageIdentity(baseInput({ enterpriseApiKey: 'wm_ent_x', tier: 3 }));
    expect(ident.auth_kind).toBe('enterprise_api_key');
    expect(ident.plan_key).toBe('enterprise');
  });

  test('anon has null plan_key', () => {
    expect(buildUsageIdentity(baseInput()).plan_key).toBeNull();
  });
});

describe('buildUsageIdentity — auth_kind branches', () => {
  test('user_api_key takes precedence over every other signal', () => {
    const ident = buildUsageIdentity(baseInput({
      isUserApiKey: true,
      sessionUserId: 'user_123',
      userApiKeyCustomerRef: 'customer_abc',
      enterpriseApiKey: 'should-be-ignored',
      widgetKey: 'should-be-ignored',
      tier: 2,
    }));
    expect(ident.auth_kind).toBe('user_api_key');
    expect(ident.principal_id).toBe('user_123');
    expect(ident.customer_id).toBe('customer_abc');
    expect(ident.tier).toBe(2);
  });

  test('user_api_key falls back to sessionUserId for customer_id when no explicit ref', () => {
    const ident = buildUsageIdentity(baseInput({
      isUserApiKey: true,
      sessionUserId: 'user_123',
      tier: 1,
    }));
    expect(ident.customer_id).toBe('user_123');
  });

  test('clerk_jwt: customer_id prefers org over user when org is present', () => {
    const ident = buildUsageIdentity(baseInput({
      sessionUserId: 'user_123',
      clerkOrgId: 'org_acme',
      tier: 1,
    }));
    expect(ident.auth_kind).toBe('clerk_jwt');
    expect(ident.principal_id).toBe('user_123');
    expect(ident.customer_id).toBe('org_acme');
    expect(ident.tier).toBe(1);
  });

  test('clerk_jwt: customer_id falls back to user when no org', () => {
    const ident = buildUsageIdentity(baseInput({
      sessionUserId: 'user_123',
    }));
    expect(ident.customer_id).toBe('user_123');
    expect(ident.tier).toBe(0);
  });

  test('enterprise_api_key: principal_id is hashed, not raw', () => {
    const ident = buildUsageIdentity(baseInput({
      enterpriseApiKey: 'wm_super_secret_key',
      tier: 3,
    }));
    expect(ident.auth_kind).toBe('enterprise_api_key');
    expect(ident.principal_id).not.toBe('wm_super_secret_key');
    expect(ident.principal_id).toMatch(/^[0-9a-z]+$/);
    // Customer is the unmapped sentinel until a real entry is added to ENTERPRISE_KEY_TO_CUSTOMER
    expect(ident.customer_id).toBe('enterprise-unmapped');
    expect(ident.tier).toBe(3);
  });

  test('widget_key: customer_id is the widget key itself, principal_id is hashed', () => {
    const ident = buildUsageIdentity(baseInput({
      widgetKey: 'widget_pub_xyz',
    }));
    expect(ident.auth_kind).toBe('widget_key');
    expect(ident.customer_id).toBe('widget_pub_xyz');
    expect(ident.principal_id).not.toBe('widget_pub_xyz');
    expect(ident.principal_id).toMatch(/^[0-9a-z]+$/);
    expect(ident.tier).toBe(0);
  });

  test('anon: every field null, tier always zero', () => {
    const ident = buildUsageIdentity(baseInput());
    expect(ident.auth_kind).toBe('anon');
    expect(ident.principal_id).toBeNull();
    expect(ident.customer_id).toBeNull();
    expect(ident.tier).toBe(0);
  });

  test('anon: tier coerces to 0 even if input.tier was set (defensive)', () => {
    // No identity signal but a leftover tier value should not show up as a mystery free row.
    const ident = buildUsageIdentity(baseInput({ tier: 99 }));
    expect(ident.tier).toBe(0);
  });
});

describe('buildUsageIdentity — tier handling', () => {
  test('null tier coerces to 0 for non-anon kinds', () => {
    const ident = buildUsageIdentity(baseInput({ sessionUserId: 'u', tier: null }));
    expect(ident.tier).toBe(0);
  });

  test('zero tier is preserved (not promoted)', () => {
    const ident = buildUsageIdentity(baseInput({ sessionUserId: 'u', tier: 0 }));
    expect(ident.tier).toBe(0);
  });

  test('integer tiers pass through unchanged', () => {
    for (const t of [0, 1, 2, 3]) {
      const ident = buildUsageIdentity(baseInput({ sessionUserId: 'u', tier: t }));
      expect(ident.tier).toBe(t);
    }
  });
});

describe('buildUsageIdentity — secret handling', () => {
  test('enterprise key never appears verbatim in any output field', () => {
    const secret = 'wm_ent_LEAKY_VALUE_DO_NOT_LOG';
    const ident = buildUsageIdentity(baseInput({ enterpriseApiKey: secret }));
    expect(JSON.stringify(ident)).not.toContain(secret);
  });

  test('widget key appears as customer_id (intentional — widget keys are public)', () => {
    // Widget keys are embeds installed on third-party sites; treating them as
    // customer attribution is the contract documented in usage-identity.ts:73-79.
    const ident = buildUsageIdentity(baseInput({ widgetKey: 'widget_public_xyz' }));
    expect(ident.customer_id).toBe('widget_public_xyz');
  });
});
