import { HttpStatus, Injectable } from '@nestjs/common';
import { DatabaseService, logger } from '../../config';
import {
  MemberStatusEnum,
  ProbationOutcomeEnum,
  ProbationStatusEnum,
} from '../../config/database/entities';
import { createGeneralError } from '../../utils';
import { NotificationsService } from '../notifications/notifications.service';
import {
  createProbationDto,
  filterProbationDto,
  reviewProbationDto,
} from './probation.dto';

@Injectable()
export class ProbationService {
  constructor(
    private readonly db: DatabaseService,
    private readonly notificationsService: NotificationsService,
  ) {}

  async createProbation(data: createProbationDto, ownerId: string) {
    try {
      const member = await this.db.members.findFirst({
        where: { id: data.member_id, owner_id: ownerId },
      });
      if (!member) {
        throw createGeneralError('Member not found', 'general', HttpStatus.NOT_FOUND);
      }

      const startDate = data.start_date ? new Date(data.start_date) : new Date();
      const months = data.duration_months || 3;
      const calcEnd = new Date(startDate);
      calcEnd.setMonth(calcEnd.getMonth() + months);

      const reviewDue = new Date(calcEnd.getTime() - 7 * 24 * 60 * 60 * 1000);

      const probation = await this.db.probations.create({
        data: {
          member_id: data.member_id,
          onboarding_case_id: data.onboarding_case_id || null,
          start_date: startDate,
          duration_months: months,
          calculated_end_date: calcEnd,
          current_end_date: calcEnd,
          status: ProbationStatusEnum.pending,
          manager_id: data.manager_id || member.reporting_manager_id || null,
          review_due_date: reviewDue,
          notes: data.notes || null,
          owner_id: ownerId,
        },
      });

      return this.getProbationById(probation.id, ownerId);
    } catch (error) {
      logger.error('Failed to create probation record', error);
      throw error;
    }
  }

  async getProbationsByFilter(query: filterProbationDto, ownerId: string) {
    try {
      const page = query.page || 1;
      const limit = query.limit || 10;
      const skip = (page - 1) * limit;

      const where: any = { owner_id: ownerId };
      if (query.status) where.status = query.status;
      if (query.member_id) where.member_id = query.member_id;

      const total = await this.db.probations.count({ where });
      const items = await this.db.probations.findMany({
        where,
        include: {
          member: { include: { department: true, designation: true } },
          manager: true,
          reviews: true,
        },
        orderBy: { created_at: 'desc' },
        skip,
        take: limit,
      });

      return {
        data: items,
        pagination: {
          total,
          page,
          limit,
          totalPages: Math.ceil(total / limit),
        },
      };
    } catch (error) {
      logger.error('Failed to fetch probation records', error);
      throw error;
    }
  }

  async getProbationById(id: string, ownerId: string) {
    const probation = await this.db.probations.findFirst({
      where: { id, owner_id: ownerId },
      include: {
        member: { include: { department: true, designation: true, reporting_manager: true } },
        manager: true,
        reviews: true,
      },
    });

    if (!probation) {
      throw createGeneralError('Probation record not found', 'general', HttpStatus.NOT_FOUND);
    }
    return probation;
  }

  async reviewProbation(
    id: string,
    data: reviewProbationDto,
    reviewerId: string,
    ownerId: string,
  ) {
    const probation = await this.getProbationById(id, ownerId);

    const now = new Date();

    if (data.outcome === ProbationOutcomeEnum.pass) {
      await this.db.probations.update({
        where: { id },
        data: {
          status: ProbationStatusEnum.passed,
          final_outcome: ProbationOutcomeEnum.pass,
          reviewed_at: now,
          notes: data.comments || probation.notes,
        },
      });

      // Send notification
      try {
        await this.notificationsService.sendNotification({
          owner_id: ownerId,
          title: 'Probation Passed',
          message: `Congratulations! ${probation.member?.first_name} ${probation.member?.last_name} has successfully passed probation.`,
          type: 'RECRUITMENT' as any,
          member_id: probation.member_id,
          reference_id: id,
        });
      } catch (e) {
        logger.warn('Failed to send probation pass notification', e);
      }
    } else if (data.outcome === ProbationOutcomeEnum.extend) {
      const extMonths = data.extension_months || 1;
      const prevEnd = new Date(probation.current_end_date);
      const newEnd = new Date(prevEnd);
      newEnd.setMonth(newEnd.getMonth() + extMonths);

      const reviewDue = new Date(newEnd.getTime() - 7 * 24 * 60 * 60 * 1000);

      // Preserve historical audit review
      await this.db.probation_reviews.create({
        data: {
          probation_id: id,
          previous_end_date: prevEnd,
          extension_months: extMonths,
          new_end_date: newEnd,
          review_date: now,
          reviewer_id: reviewerId,
          outcome: ProbationOutcomeEnum.extend,
          comments: data.comments || null,
        },
      });

      await this.db.probations.update({
        where: { id },
        data: {
          status: ProbationStatusEnum.extended,
          current_end_date: newEnd,
          review_due_date: reviewDue,
          reviewed_at: now,
          notes: data.comments || probation.notes,
        },
      });
    } else if (data.outcome === ProbationOutcomeEnum.fail) {
      await this.db.probations.update({
        where: { id },
        data: {
          status: ProbationStatusEnum.failed,
          final_outcome: ProbationOutcomeEnum.fail,
          reviewed_at: now,
          notes: data.comments || probation.notes,
        },
      });
    }

    return this.getProbationById(id, ownerId);
  }
}
