import {
  Injectable,
  BadRequestException,
  ForbiddenException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between, IsNull, ILike } from 'typeorm';
import { Attendance } from './attendance.entity';
import { User } from '../users/user.entity';
import { CheckInDto, CheckOutDto } from './dto/attendance.dto';
import { NotificationsService } from 'src/notifications/notifications.service';
import { AttendanceMethod } from 'common/enums/attendance-method.enum';
import { PaginationQueryDto } from 'common/dto/pagination-query.dto';

@Injectable()
export class AttendanceService {
  constructor(
    @InjectRepository(Attendance)
    private attendanceRepository: Repository<Attendance>,
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private notificationsService: NotificationsService,
  ) {}

  async checkIn(userId: number, checkInDto: CheckInDto) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) throw new BadRequestException('User not found');

    // Checking the attendance method
    if (user.attendanceMethod !== AttendanceMethod.GPS) {
      throw new ForbiddenException(
        'You are assigned to biometric attendance. Please use the biometric device.',
      );
    }

    // Check that no login is open
    const openRecord = await this.attendanceRepository.findOne({
      where: { userId, checkOut: IsNull() },
    });
    if (openRecord) {
      throw new BadRequestException(
        'You already have an open check-in. Please check out first.',
      );
    }

    // Geographic location verification can be added here (for example, via a maps service)
    const attendance = this.attendanceRepository.create({
      userId,
      checkIn: new Date(),
      checkInLat: checkInDto.latitude,
      checkInLng: checkInDto.longitude,
      method: AttendanceMethod.GPS,
    });

    const saved = await this.attendanceRepository.save(attendance);
    return saved;
  }

  async checkOut(userId: number, checkOutDto: CheckOutDto) {
    const openRecord = await this.attendanceRepository.findOne({
      where: { userId, checkOut: IsNull() },
    });
    if (!openRecord) {
      throw new BadRequestException('No open check-in found');
    }

    openRecord.checkOut = new Date();
    openRecord.checkOutLat = checkOutDto.latitude ?? 0;
    openRecord.checkOutLng = checkOutDto.longitude ?? 0;
    const saved = await this.attendanceRepository.save(openRecord);
    return saved;
  }

  async getTodayAttendance(userId: number) {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);

    return this.attendanceRepository.find({
      where: {
        userId,
        checkIn: Between(today, tomorrow),
      },
    });
  }

  async getUserAttendance(userId: number, query: PaginationQueryDto) {
    const { page = 1, limit = 10, sortBy = 'checkIn', sortDirection = 'DESC' } = query;
    const skip = (page - 1) * limit;

    // Fix: If sorting by 'user' (object), default to checkIn
    const effectiveSortBy = sortBy === 'user' ? 'checkIn' : sortBy;

    const [data, total] = await this.attendanceRepository.findAndCount({
      where: { userId },
      order: { [effectiveSortBy]: sortDirection } as any,
      skip,
      take: limit,
    });

    return { data, total };
  }

  async getAllAttendance(query: PaginationQueryDto) {
    const { page = 1, limit = 10, search = '', sortBy = 'checkIn', sortDirection = 'DESC' } = query;
    const skip = (page - 1) * limit;

    let where: any = {};
    if (search) {
      where = [
        { user: { name: ILike(`%${search}%`) } },
        { user: { email: ILike(`%${search}%`) } },
      ];
    }

    const [data, total] = await this.attendanceRepository.findAndCount({
      where,
      relations: ['user'],
      order: { [sortBy === 'user' ? 'checkIn' : sortBy]: sortDirection } as any,
      skip,
      take: limit,
    });

    return { data, total };
  }

  async createBiometricAttendance(
    userId: number,
    timestamp: Date,
    type: string,
  ) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user || user.attendanceMethod !== AttendanceMethod.BIOMETRIC) {
      throw new BadRequestException('User not assigned to biometric method');
    }

    if (type === 'check-in') {
      const openRecord = await this.attendanceRepository.findOne({
        where: { userId, checkOut: IsNull() },
      });
      if (openRecord) {
        throw new BadRequestException('Already checked in');
      }
      const attendance = this.attendanceRepository.create({
        userId,
        checkIn: timestamp,
        method: AttendanceMethod.BIOMETRIC,
      });
      return this.attendanceRepository.save(attendance);
    } else if (type === 'check-out') {
      const openRecord = await this.attendanceRepository.findOne({
        where: { userId, checkOut: IsNull() },
      });
      if (!openRecord) {
        throw new BadRequestException('No open check-in');
      }
      openRecord.checkOut = timestamp;
      return this.attendanceRepository.save(openRecord);
    }
  }
}
