import {
  Injectable,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { PrismaService } from '../../prisma/prisma.service';
import { UpdateUserDto } from './dto/update-user.dto';
import { ChangePasswordDto } from './dto/change-password.dto';
import { UpdateNotificationSettingsDto } from './dto/update-notification-settings.dto';

const BCRYPT_ROUNDS = 10;

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

  private serializeBigInt(data: any): any {
    return JSON.parse(
      JSON.stringify(data, (_, v) => (typeof v === 'bigint' ? Number(v) : v)),
    );
  }

  async getProfile(userId: number) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      include: {
        business: true,
        subscription: { include: { plan: true } },
        creditSettings: true,
      },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const {
      passwordHash,
      refreshToken,
      emailVerifyToken,
      passwordResetToken,
      passwordResetExpires,
      ...profile
    } = user;

    return this.serializeBigInt(profile);
  }

  async updateProfile(userId: number, dto: UpdateUserDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const updated = await this.prisma.user.update({
      where: { id: userId },
      data: dto,
    });

    const {
      passwordHash,
      refreshToken,
      emailVerifyToken,
      passwordResetToken,
      passwordResetExpires,
      ...profile
    } = updated;

    return this.serializeBigInt(profile);
  }

  async changePassword(userId: number, dto: ChangePasswordDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const isValid = await bcrypt.compare(dto.currentPassword, user.passwordHash);
    if (!isValid) {
      throw new BadRequestException('Current password is incorrect');
    }

    const newHash = await bcrypt.hash(dto.newPassword, BCRYPT_ROUNDS);

    await this.prisma.user.update({
      where: { id: userId },
      data: { passwordHash: newHash },
    });

    return { message: 'Password changed successfully' };
  }

  async getNotificationSettings(userId: number) {
    let settings = await this.prisma.notificationSettings.findUnique({
      where: { userId },
    });

    if (!settings) {
      settings = await this.prisma.notificationSettings.create({
        data: { userId },
      });
    }

    // Return frontend-friendly field names
    return {
      email: settings.emailEnabled,
      push: settings.pushEnabled,
      sms: settings.smsEnabled,
      settlement: settings.settlementAlerts,
      document: settings.documentAlerts,
      tax: settings.taxAlerts,
    };
  }

  async updateNotificationSettings(
    userId: number,
    dto: UpdateNotificationSettingsDto,
  ) {
    const data: any = {};
    if (dto.email !== undefined) data.emailEnabled = dto.email;
    if (dto.push !== undefined) data.pushEnabled = dto.push;
    if (dto.sms !== undefined) data.smsEnabled = dto.sms;
    if (dto.settlement !== undefined) data.settlementAlerts = dto.settlement;
    if (dto.document !== undefined) data.documentAlerts = dto.document;
    if (dto.tax !== undefined) data.taxAlerts = dto.tax;

    const settings = await this.prisma.notificationSettings.upsert({
      where: { userId },
      update: data,
      create: {
        userId,
        ...data,
      },
    });

    return {
      email: settings.emailEnabled,
      push: settings.pushEnabled,
      sms: settings.smsEnabled,
      settlement: settings.settlementAlerts,
      document: settings.documentAlerts,
      tax: settings.taxAlerts,
    };
  }

  async deleteAccount(userId: number) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    await this.prisma.user.update({
      where: { id: userId },
      data: {
        isActive: false,
        refreshToken: null,
      },
    });

    return { message: 'Account has been deactivated' };
  }
}
