import { randomBytes } from 'node:crypto';

import {
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';

import { hashToken, verifyTokenHash } from '@hexahost/auth';
import type { ConsoleTokenResponse } from '@hexahost/contracts';
import { getConfig } from '@hexahost/config';

interface ConsoleTokenRecord {
  serverId: string;
  userId: string;
  tokenHash: string;
  expiresAt: Date;
}

const TOKEN_TTL_MS = 5 * 60 * 1000;

@Injectable()
export class ConsoleService {
  private readonly tokens = new Map<string, ConsoleTokenRecord>();

  issueToken(serverId: string, userId: string): ConsoleTokenResponse {
    const token = randomBytes(32).toString('base64url');
    const tokenHash = hashToken(token);
    const expiresAt = new Date(Date.now() + TOKEN_TTL_MS);

    this.tokens.set(tokenHash, {
      serverId,
      userId,
      tokenHash,
      expiresAt,
    });

    const config = getConfig();
    const wsUrl = `${config.API_URL.replace(/^http/, 'ws')}/api/v1/servers/${serverId}/console/ws`;

    return {
      token,
      expiresAt: expiresAt.toISOString(),
      wsUrl,
    };
  }

  validateToken(
    token: string,
    serverId: string,
  ): { userId: string } {
    const now = new Date();
    this.pruneExpired(now);

    for (const record of this.tokens.values()) {
      if (
        record.serverId === serverId &&
        record.expiresAt > now &&
        verifyTokenHash(token, record.tokenHash)
      ) {
        return { userId: record.userId };
      }
    }

    throw new UnauthorizedException('Invalid or expired console token');
  }

  private pruneExpired(now: Date): void {
    for (const [hash, record] of this.tokens.entries()) {
      if (record.expiresAt <= now) {
        this.tokens.delete(hash);
      }
    }
  }
}
