import { randomBytes } from 'crypto';
import { getDataSource } from '../db/database';
import { SessionTokenEntity } from '../entities/SessionTokenEntity';
import { getUserById } from '../cqrs';
import type { AuthUserPayload } from '../cqrs/types';

const DEFAULT_TOKEN_TTL_MS = 10 * 365 * 24 * 60 * 60 * 1000;

export interface IssuedSessionToken {
  token: string;
  userId: string;
  issuedAt: number;
  expiresAt: number;
}

export interface AuthenticatedSession {
  token: string;
  user: AuthUserPayload;
  issuedAt: number;
  expiresAt: number;
}

function getTokenRepository() {
  return getDataSource().getRepository(SessionTokenEntity);
}

export function getSessionTokenTtlMs(): number {
  const configured = Number(process.env.SESSION_TOKEN_TTL_MS);

  return Number.isFinite(configured) && configured > 0
    ? configured
    : DEFAULT_TOKEN_TTL_MS;
}

export async function issueSessionToken(userId: string): Promise<IssuedSessionToken> {
  const token = randomBytes(32).toString('hex');
  const issuedAt = Date.now();
  const expiresAt = issuedAt + getSessionTokenTtlMs();
  const repo = getTokenRepository();

  await repo.save(repo.create({
    token,
    userId,
    issuedAt,
    expiresAt
  }));

  return { token, userId, issuedAt, expiresAt };
}

export async function consumeSessionToken(token: string): Promise<AuthenticatedSession | null> {
  const normalized = token.trim();

  if (!normalized) {
    return null;
  }

  const repo = getTokenRepository();
  const record = await repo.findOne({ where: { token: normalized } });

  if (!record || record.expiresAt < Date.now()) {
    if (record) {
      await repo.delete({ token: normalized });
    }

    return null;
  }

  const user = await getUserById(record.userId);

  if (!user) {
    await repo.delete({ token: normalized });
    return null;
  }

  return {
    token: record.token,
    user,
    issuedAt: record.issuedAt,
    expiresAt: record.expiresAt
  };
}

export async function revokeSessionToken(token: string): Promise<void> {
  const normalized = token.trim();

  if (!normalized) {
    return;
  }

  await getTokenRepository().delete({ token: normalized });
}

export async function revokeAllSessionTokensForUser(userId: string): Promise<void> {
  await getTokenRepository().delete({ userId });
}

export async function pruneExpiredSessionTokens(): Promise<void> {
  const repo = getTokenRepository();
  const now = Date.now();
  const expired = await repo.createQueryBuilder('token')
    .where('token.expiresAt < :now', { now })
    .getMany();

  if (expired.length === 0) {
    return;
  }

  await repo.remove(expired);
}
