import { Injectable, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { generateSecret, generateURI, verifySync } from 'otplib';
import * as QRCode from 'qrcode';

export interface TotpSetupResult {
  secret: string;
  otpauthUrl: string;
  qrCodeDataUrl: string;
}

@Injectable()
export class TotpService {
  constructor(private readonly prisma: PrismaService) {}

  async setup(userId: string): Promise<TotpSetupResult> {
    const users = await this.prisma.$queryRawUnsafe<
      { id: string; phone: string; totpSecret: string | null }[]
    >(
      `SELECT id, phone, "totpSecret" FROM users WHERE id = $1 LIMIT 1`,
      userId,
    );

    if (!users.length) {
      throw new BadRequestException('User not found');
    }

    const user = users[0];

    if (user.totpSecret) {
      throw new BadRequestException('2FA is already enabled');
    }

    const secret = generateSecret();
    const otpauthUrl = generateURI({
      issuer: 'MyFitCRM',
      label: user.phone,
      secret,
    });
    const qrCodeDataUrl = await QRCode.toDataURL(otpauthUrl);

    return { secret, otpauthUrl, qrCodeDataUrl };
  }

  async verify(userId: string, token: string, secret: string): Promise<void> {
    const result = verifySync({ token, secret });

    if (!result.valid) {
      throw new BadRequestException('Invalid TOTP code');
    }

    await this.prisma.$executeRawUnsafe(
      `UPDATE users SET "totpSecret" = $1, "updatedAt" = NOW() WHERE id = $2`,
      secret,
      userId,
    );
  }

  async validate(userId: string, token: string): Promise<boolean> {
    const users = await this.prisma.$queryRawUnsafe<
      { totpSecret: string | null }[]
    >(
      `SELECT "totpSecret" FROM users WHERE id = $1 LIMIT 1`,
      userId,
    );

    if (!users.length || !users[0].totpSecret) {
      throw new BadRequestException('2FA is not enabled');
    }

    const result = verifySync({
      token,
      secret: users[0].totpSecret,
    });
    return result.valid;
  }

  async disable(userId: string, token: string): Promise<void> {
    const isValid = await this.validate(userId, token);

    if (!isValid) {
      throw new BadRequestException('Invalid TOTP code');
    }

    await this.prisma.$executeRawUnsafe(
      `UPDATE users SET "totpSecret" = NULL, "updatedAt" = NOW() WHERE id = $1`,
      userId,
    );
  }
}
