/**
 * Tests for API tokens module — Zod schemas + type validation.
 */

import { describe, it, expect } from 'vitest';
import { CreateTokenSchema, PatchTokenSchema } from './types.js';
import type { ApiTokenDoc, ApiTokenResponse } from './types.js';

describe('CreateTokenSchema', () => {
  it('accepts valid input with defaults', () => {
    const result = CreateTokenSchema.safeParse({ name: 'CI Pipeline' });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.name).toBe('CI Pipeline');
      expect(result.data.tokenType).toBe('product_api');
      expect(result.data.environment).toBeUndefined();
      expect(result.data.scopes).toEqual(['read']);
      expect(result.data.expiresInDays).toBe(90);
    }
  });

  it('accepts full input', () => {
    const result = CreateTokenSchema.safeParse({
      name: 'Deploy Key',
      tokenType: 'service_api',
      environment: 'staging',
      scopes: ['read', 'write', 'admin'],
      expiresInDays: 30,
    });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.tokenType).toBe('service_api');
      expect(result.data.environment).toBe('staging');
      expect(result.data.scopes).toEqual(['read', 'write', 'admin']);
      expect(result.data.expiresInDays).toBe(30);
    }
  });

  it('rejects empty name', () => {
    const result = CreateTokenSchema.safeParse({ name: '' });
    expect(result.success).toBe(false);
  });

  it('rejects missing name', () => {
    const result = CreateTokenSchema.safeParse({});
    expect(result.success).toBe(false);
  });

  it('rejects expiresInDays < 1', () => {
    const result = CreateTokenSchema.safeParse({ name: 'test', expiresInDays: 0 });
    expect(result.success).toBe(false);
  });

  it('rejects expiresInDays > 365', () => {
    const result = CreateTokenSchema.safeParse({ name: 'test', expiresInDays: 400 });
    expect(result.success).toBe(false);
  });

  it('rejects non-integer expiresInDays', () => {
    const result = CreateTokenSchema.safeParse({ name: 'test', expiresInDays: 30.5 });
    expect(result.success).toBe(false);
  });

  it('accepts boundary values for expiresInDays', () => {
    const min = CreateTokenSchema.safeParse({ name: 'test', expiresInDays: 1 });
    expect(min.success).toBe(true);

    const max = CreateTokenSchema.safeParse({ name: 'test', expiresInDays: 365 });
    expect(max.success).toBe(true);
  });

  it('accepts empty scopes array', () => {
    const result = CreateTokenSchema.safeParse({ name: 'test', scopes: [] });
    expect(result.success).toBe(true);
  });
});

describe('PatchTokenSchema', () => {
  it('accepts revoke action', () => {
    const result = PatchTokenSchema.safeParse({ action: 'revoke' });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.action).toBe('revoke');
    }
  });

  it('rejects unknown action', () => {
    const result = PatchTokenSchema.safeParse({ action: 'delete' });
    expect(result.success).toBe(false);
  });

  it('rejects empty action', () => {
    const result = PatchTokenSchema.safeParse({ action: '' });
    expect(result.success).toBe(false);
  });

  it('rejects missing action', () => {
    const result = PatchTokenSchema.safeParse({});
    expect(result.success).toBe(false);
  });
});

describe('ApiTokenDoc type shape', () => {
  it('has all required fields', () => {
    const doc: ApiTokenDoc = {
      id: 'tok_abc123',
      productId: 'lysnrai',
      userId: 'user_1',
      userName: 'admin@example.com',
      name: 'CI Token',
      tokenType: 'service_api',
      environment: 'prod',
      prefix: 'wai_abc12345',
      tokenHash: '$2a$10$hashedvalue',
      status: 'active',
      scopes: ['read', 'write'],
      createdAt: '2026-01-01T00:00:00.000Z',
      expiresAt: '2026-04-01T00:00:00.000Z',
      lastUsed: null,
      ttl: 7776000,
    };

    expect(doc.status).toBe('active');
    expect(doc.tokenType).toBe('service_api');
    expect(doc.environment).toBe('prod');
    expect(doc.scopes).toContain('read');
    expect(doc.lastUsed).toBeNull();
  });

  it('supports all status values', () => {
    const statuses: ApiTokenDoc['status'][] = ['active', 'revoked', 'expired'];
    for (const status of statuses) {
      const doc: ApiTokenDoc = {
        id: 'tok_1',
        productId: 'lysnrai',
        userId: 'u1',
        userName: 'test',
        name: 'test',
        tokenType: 'user_api',
        environment: 'prod',
        prefix: 'wai_',
        tokenHash: 'hash',
        status,
        scopes: [],
        createdAt: '',
        expiresAt: '',
        lastUsed: null,
      };
      expect(doc.status).toBe(status);
    }
  });
});

describe('ApiTokenResponse type shape', () => {
  it('excludes tokenHash from the response', () => {
    const doc: ApiTokenDoc = {
      id: 'tok_1',
      productId: 'lysnrai',
      userId: 'u1',
      userName: 'test@test.com',
      name: 'My Token',
      tokenType: 'product_api',
      environment: 'staging',
      prefix: 'wai_abc',
      tokenHash: 'secret_hash',
      status: 'active',
      scopes: ['read'],
      createdAt: '2026-01-01T00:00:00.000Z',
      expiresAt: '2026-04-01T00:00:00.000Z',
      lastUsed: '2026-02-15T10:00:00.000Z',
    };

    // Simulate what stripHash does
    const { tokenHash: _tokenHash, ...response } = doc;
    void _tokenHash;
    const apiResponse: ApiTokenResponse = response;

    expect(apiResponse.id).toBe('tok_1');
    expect(apiResponse.name).toBe('My Token');
    expect(apiResponse.tokenType).toBe('product_api');
    expect(apiResponse.environment).toBe('staging');
    expect('tokenHash' in apiResponse).toBe(false);
  });
});
