/**
 * Auth Middleware Tests
 * Tests for dashboard authentication middleware and routes.
 */

import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import bcrypt from 'bcrypt';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { getDashboardAuthConfig } from '../../../src/config/unified-config-loader';
import { runWithScopedConfigDir } from '../../../src/utils/config-manager';

describe('Dashboard Auth', () => {
  let tempDir = '';

  beforeEach(() => {
    tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-dashboard-auth-'));
  });

  afterEach(() => {
    if (tempDir && fs.existsSync(tempDir)) {
      fs.rmSync(tempDir, { recursive: true, force: true });
    }
  });

  describe('getDashboardAuthConfig', () => {
    it('returns disabled by default', async () => {
      const config = await runWithScopedConfigDir(tempDir, () => getDashboardAuthConfig());
      expect(config.enabled).toBe(false);
    });

    it('returns 24 hour default session timeout', async () => {
      const config = await runWithScopedConfigDir(tempDir, () => getDashboardAuthConfig());
      expect(config.session_timeout_hours).toBe(24);
    });
  });

  describe('bcrypt password hashing', () => {
    it('generates valid bcrypt hash', async () => {
      const password = 'testpassword123';
      const hash = await bcrypt.hash(password, 10);

      expect(hash).toMatch(/^\$2[aby]\$\d{2}\$.{53}$/);
    });

    it('verifies correct password', async () => {
      const password = 'testpassword123';
      const hash = await bcrypt.hash(password, 10);

      const isValid = await bcrypt.compare(password, hash);
      expect(isValid).toBe(true);
    });

    it('rejects incorrect password', async () => {
      const password = 'testpassword123';
      const hash = await bcrypt.hash(password, 10);

      const isValid = await bcrypt.compare('wrongpassword', hash);
      expect(isValid).toBe(false);
    });

    it('timing-safe comparison for wrong password', async () => {
      const password = 'testpassword123';
      const hash = await bcrypt.hash(password, 10);

      // bcrypt.compare should take similar time for wrong vs right password
      // This is a basic check that the function works
      const start1 = performance.now();
      await bcrypt.compare('wrongpassword', hash);
      const time1 = performance.now() - start1;

      const start2 = performance.now();
      await bcrypt.compare(password, hash);
      const time2 = performance.now() - start2;

      // Both should complete (timing comparison is handled by bcrypt internally)
      expect(time1).toBeGreaterThan(0);
      expect(time2).toBeGreaterThan(0);
    });
  });

  describe('public paths', () => {
    const PUBLIC_PATHS = ['/api/auth/login', '/api/auth/check', '/api/auth/setup', '/api/health'];

    it('identifies public paths correctly', () => {
      const isPublicPath = (path: string) =>
        PUBLIC_PATHS.some((p) => path.toLowerCase().startsWith(p));

      expect(isPublicPath('/api/auth/login')).toBe(true);
      expect(isPublicPath('/api/auth/check')).toBe(true);
      expect(isPublicPath('/api/auth/setup')).toBe(true);
      expect(isPublicPath('/api/health')).toBe(true);
      expect(isPublicPath('/api/profiles')).toBe(false);
      expect(isPublicPath('/api/config')).toBe(false);
    });

    it('handles case-insensitive paths', () => {
      const isPublicPath = (path: string) =>
        PUBLIC_PATHS.some((p) => path.toLowerCase().startsWith(p));

      expect(isPublicPath('/API/AUTH/LOGIN')).toBe(true);
      expect(isPublicPath('/Api/Health')).toBe(true);
    });
  });

  describe('session configuration', () => {
    it('session timeout converts to milliseconds correctly', () => {
      const hours = 24;
      const maxAge = hours * 60 * 60 * 1000;

      expect(maxAge).toBe(86400000); // 24 hours in ms
    });

    it('custom session timeout works', () => {
      const hours = 8;
      const maxAge = hours * 60 * 60 * 1000;

      expect(maxAge).toBe(28800000); // 8 hours in ms
    });
  });

  describe('auth flow logic', () => {
    it('bypasses auth when disabled', () => {
      const shouldSkip = true;
      expect(shouldSkip).toBe(true);
    });

    it('requires auth when enabled', () => {
      const authConfig = {
        enabled: true,
        username: 'admin',
        password_hash: '$2b$10$test',
      };
      const shouldSkip = !authConfig.enabled;
      expect(shouldSkip).toBe(false);
    });

    it('validates username match', () => {
      const authConfig = { username: 'admin' };
      const usernameMatch = 'admin' === authConfig.username;
      expect(usernameMatch).toBe(true);
    });

    it('rejects wrong username', () => {
      const authConfig = { username: 'admin' };
      const usernameMatch = 'wrong' === authConfig.username;
      expect(usernameMatch).toBe(false);
    });
  });

  describe('rate limiting config', () => {
    it('rate limit window is 15 minutes', () => {
      const windowMs = 15 * 60 * 1000;
      expect(windowMs).toBe(900000);
    });

    it('max attempts is 5', () => {
      const maxAttempts = 5;
      expect(maxAttempts).toBe(5);
    });
  });
});
