import { assert, assertEquals, fail } from '@std/assert';

import * as plugins from '../ts/plugins.ts';
import type { IUser as IDatabaseUser } from '../ts/types.ts';
import { AdminHandler } from '../ts/opsserver/handlers/admin.handler.ts';
import {
  hashPassword,
  isPbkdf2Hash,
  verifyPassword,
} from '../ts/utils/auth.ts';

class FakeDatabase {
  constructor(private users: Map<string, IDatabaseUser>) {}

  getUserByUsername(username: string): IDatabaseUser | null {
    return this.users.get(username) ?? null;
  }

  updateUserPassword(username: string, passwordHash: string): void {
    const user = this.users.get(username);
    if (!user) {
      return;
    }

    this.users.set(username, {
      ...user,
      passwordHash,
      updatedAt: Date.now(),
    });
  }
}

async function createAdminHandler(users: IDatabaseUser[]): Promise<AdminHandler> {
  const userMap = new Map(users.map((user) => [user.username, user]));
  const fakeOpsServer = {
    typedrouter: new plugins.typedrequest.TypedRouter(),
    oneboxRef: {
      database: new FakeDatabase(userMap),
    },
  };

  const adminHandler = new AdminHandler(fakeOpsServer as any);
  await adminHandler.initialize();
  return adminHandler;
}

Deno.test('password helpers support PBKDF2 password hashes', async () => {
  const password = 'correct horse battery staple';
  const passwordHash = await hashPassword(password);

  assert(isPbkdf2Hash(passwordHash));
  assert(await verifyPassword(password, passwordHash));
  assert(!(await verifyPassword('wrong password', passwordHash)));
  assert(!(await verifyPassword(password, btoa(password))));
});

Deno.test('verified identity is derived from the signed JWT and database, not client fields', async () => {
  const adminHandler = await createAdminHandler([
    {
      id: 1,
      username: 'alice',
      passwordHash: await hashPassword('password123'),
      role: 'user',
      createdAt: Date.now(),
      updatedAt: Date.now(),
    },
  ]);

  const expiresAt = Date.now() + 60_000;
  const jwt = await adminHandler.smartjwtInstance.createJWT({
    userId: '1',
    username: 'alice',
    role: 'user',
    status: 'loggedIn',
    expiresAt,
  });

  const verifiedIdentity = await adminHandler.getVerifiedIdentity({
    jwt,
    userId: '999',
    username: 'mallory',
    role: 'admin',
    expiresAt: 0,
  });

  assertEquals(verifiedIdentity.userId, '1');
  assertEquals(verifiedIdentity.username, 'alice');
  assertEquals(verifiedIdentity.role, 'user');
  assertEquals(verifiedIdentity.expiresAt, expiresAt);

  let rejected = false;
  try {
    await adminHandler.getVerifiedAdminIdentity(verifiedIdentity);
    fail('Expected admin-only identity verification to reject non-admin users');
  } catch {
    rejected = true;
  }

  assert(rejected);
});
