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

import { PrismaService } from '@/prisma/prisma.service';

import { UpdateNotificationSettingDto } from './dto/update-notification-setting.dto';

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

  async findMySetting(userId: number) {
    const validUserId = this.validateUserId(userId);

    await this.ensureUserExists(validUserId);

    const setting = await this.prisma.notificationSetting.upsert({
      where: {
        userId: validUserId,
      },

      create: {
        userId: validUserId,

        systemEnabled: true,
        unreadBadge: true,

        gmailEnabled: false,
        gmailAddress: null,
      },

      update: {},
    });

    return this.toResponse(setting);
  }

  async updateMySetting(userId: number, dto: UpdateNotificationSettingDto) {
    const validUserId = this.validateUserId(userId);

    await this.ensureUserExists(validUserId);

    const gmailAddress =
      typeof dto.gmailAddress === 'string'
        ? dto.gmailAddress.trim().toLowerCase()
        : null;

    if (dto.gmailEnabled && !gmailAddress) {
      throw new BadRequestException(
        'Gmail address is required when Gmail notification is enabled.',
      );
    }

    const setting = await this.prisma.notificationSetting.upsert({
      where: {
        userId: validUserId,
      },

      create: {
        userId: validUserId,

        systemEnabled: dto.systemEnabled,
        unreadBadge: dto.unreadBadge,

        gmailEnabled: dto.gmailEnabled,
        gmailAddress: dto.gmailEnabled ? gmailAddress : null,
      },

      update: {
        systemEnabled: dto.systemEnabled,
        unreadBadge: dto.unreadBadge,

        gmailEnabled: dto.gmailEnabled,
        gmailAddress: dto.gmailEnabled ? gmailAddress : null,
      },
    });

    return this.toResponse(setting);
  }

  private validateUserId(userId: number): number {
    const validUserId = Number(userId);

    if (!Number.isInteger(validUserId) || validUserId <= 0) {
      throw new BadRequestException('Invalid authenticated user ID.');
    }

    return validUserId;
  }

  private async ensureUserExists(userId: number): Promise<void> {
    const user = await this.prisma.user.findUnique({
      where: {
        id: userId,
      },

      select: {
        id: true,
      },
    });

    if (!user) {
      throw new NotFoundException('Authenticated user was not found.');
    }
  }

  private toResponse(setting: {
    id: number;
    userId: number;
    systemEnabled: boolean;
    unreadBadge: boolean;
    gmailEnabled: boolean;
    gmailAddress: string | null;
    createdAt: Date;
    updatedAt: Date;
  }) {
    return {
      id: setting.id,
      userId: setting.userId,

      systemEnabled: setting.systemEnabled,
      unreadBadge: setting.unreadBadge,

      gmailEnabled: setting.gmailEnabled,
      gmailAddress: setting.gmailAddress,

      createdAt: setting.createdAt,
      updatedAt: setting.updatedAt,
    };
  }
}
