import {
  Injectable,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BiometricLog } from './biometric-log.entity';
import { Device } from './device.entity';
import { User } from '../users/user.entity';
import { AttendanceService } from '../attendance/attendance.service';
import { NotificationsService } from '../notifications/notifications.service';
import { SyncLogsDto } from './dto/register-device.dto';
import { AttendanceMethod } from 'common/enums/attendance-method.enum';

@Injectable()
export class BiometricService {
  constructor(
    @InjectRepository(BiometricLog)
    private biometricLogRepository: Repository<BiometricLog>,
    @InjectRepository(Device)
    private deviceRepository: Repository<Device>,
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private attendanceService: AttendanceService,
    private notificationsService: NotificationsService,
  ) {}

  /**
   * تسجيل جهاز بصمة جديد في النظام
   */
  async registerDevice(
    deviceId: string,
    name: string,
    ipAddress?: string,
  ): Promise<Device> {
    const existing = await this.deviceRepository.findOne({
      where: { deviceId },
    });
    if (existing) {
      throw new BadRequestException('Device already registered');
    }
    const device = this.deviceRepository.create({ deviceId, name, ipAddress });
    return this.deviceRepository.save(device);
  }

  /**
   * ربط معرف جهاز البصمة بموظف معين
   */
  async linkEmployeeToDevice(
    userId: number,
    biometricDeviceId: string,
  ): Promise<User> {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) throw new NotFoundException('User not found');

    // Verify that the user is using the fingerprint method
    if (user.attendanceMethod !== AttendanceMethod.BIOMETRIC) {
      throw new BadRequestException('User is not assigned to biometric method');
    }

    user.biometricDeviceId = biometricDeviceId;
    return this.userRepository.save(user);
  }

  /**
   * Synchronize attendance records from the fingerprint device (simulated or actual API call)
   */
  async syncLogs(syncLogsDto: SyncLogsDto): Promise<BiometricLog[]> {
    const { deviceId, logs } = syncLogsDto;

    // Device verification
    const device = await this.deviceRepository.findOne({ where: { deviceId } });
    if (!device) throw new NotFoundException('Device not registered');

    const savedLogs: BiometricLog[] = [];

    for (const log of logs) {
      // Search for the user associated with the device ID
      const user = await this.userRepository.findOne({
        where: {
          biometricDeviceId: log.userId.toString(),
          attendanceMethod: AttendanceMethod.BIOMETRIC,
        },
      });
      if (!user) {
        // Recording without linking (can be dealt with later)
        const unknownLog = this.biometricLogRepository.create({
          userId: undefined,
          deviceId,
          timestamp: log.timestamp,
          type: log.type,
        });
        await this.biometricLogRepository.save(unknownLog);
        continue;
      }

      // Checking for duplicate records
      const existing = await this.biometricLogRepository.findOne({
        where: {
          userId: user.id,
          deviceId,
          timestamp: log.timestamp,
          type: log.type,
        },
      });
      if (existing) continue;

      // Save the record in the BiometricLog table
      const biometricLog = this.biometricLogRepository.create({
        userId: user.id,
        deviceId,
        timestamp: log.timestamp,
        type: log.type,
      });
      const saved = await this.biometricLogRepository.save(biometricLog);
      savedLogs.push(saved);

      // Create an attendance record using the Attendance service
      try {
        await this.attendanceService.createBiometricAttendance(
          user.id,
          log.timestamp,
          log.type,
        );
      } catch (error) {
        console.error(
          `Failed to create attendance for user ${user.id}:`,
          (error as any).message,
        );
      }
    }

    // Notification of employees who have clocked in (optional)
    for (const log of savedLogs) {
      if (log.userId == null) {
        return []; // or handle the case as needed
      }
      const user = await this.userRepository.findOne({
        where: { id: log.userId },
      });
      if (user) {
        await this.notificationsService.createNotification({
          userId: user.id,
          title: 'Attendance Recorded',
          message: `Your ${log.type} was recorded at ${log.timestamp.toLocaleString()} via biometric device.`,
          type: 'ATTENDANCE_RECORDED' as any,
        });
      }
    }

    return savedLogs;
  }

  /**
   * Retrieve fingerprint records for a specific employee
   */
  async getUserBiometricLogs(
    userId: number,
    startDate?: Date,
    endDate?: Date,
  ): Promise<BiometricLog[]> {
    const query = this.biometricLogRepository
      .createQueryBuilder('log')
      .where('log.userId = :userId', { userId });
    if (startDate) query.andWhere('log.timestamp >= :startDate', { startDate });
    if (endDate) query.andWhere('log.timestamp <= :endDate', { endDate });
    return query.orderBy('log.timestamp', 'DESC').getMany();
  }

  /**
   * Bring all registered devices
   */
  async getDevices(): Promise<Device[]> {
    return this.deviceRepository.find();
  }

  /**
   * Update device status (active/inactive)
   */
  async updateDeviceStatus(
    deviceId: string,
    isActive: boolean,
  ): Promise<Device> {
    const device = await this.deviceRepository.findOne({ where: { deviceId } });
    if (!device) throw new NotFoundException('Device not found');
    device.isActive = isActive;
    return this.deviceRepository.save(device);
  }

  /**
   * Uploading a fingerprint record manually (in case of no direct integration)
   */
  async manualAddLog(
    userId: number,
    timestamp: Date,
    type: string,
  ): Promise<BiometricLog> {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) throw new NotFoundException('User not found');
    if (user.attendanceMethod !== AttendanceMethod.BIOMETRIC) {
      throw new BadRequestException('User is not assigned to biometric method');
    }

    const log = this.biometricLogRepository.create({
      userId,
      deviceId: 'manual',
      timestamp,
      type,
    });
    const saved = await this.biometricLogRepository.save(log);

    // Create an attendance log
    await this.attendanceService.createBiometricAttendance(
      userId,
      timestamp,
      type,
    );
    return saved;
  }
}
