import { Injectable } from '@nestjs/common';
import { AuditAction, AuditEntityType } from '@aechr/shared';
import {
  AssignmentStatus,
  DeliveryStatus,
  Prisma,
  ReminderChannel,
} from '@prisma/client';
import type { JwtAuthUser } from '../../common/interfaces/jwt-auth-user.interface';
import { buildPagination, buildSoftDeleteWhere } from '../../common/prisma/pagination';
import { AuditService } from '../audit/audit.service';
import { PrismaService } from '../prisma/prisma.service';

interface ReminderLogQuery {
  page?: number;
  limit?: number;
  includeDeleted?: boolean;
  assignmentId?: string;
  deliveryStatus?: DeliveryStatus;
}

@Injectable()
export class RemindersService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly auditService: AuditService,
  ) {}

  async listLogs(query: ReminderLogQuery) {
    const page = query.page || 1;
    const limit = query.limit || 10;
    const where: Prisma.ReminderLogWhereInput = {
      ...buildSoftDeleteWhere(query.includeDeleted),
      ...(query.assignmentId ? { assignmentId: query.assignmentId } : {}),
      ...(query.deliveryStatus ? { deliveryStatus: query.deliveryStatus } : {}),
    };

    const [items, total] = await this.prisma.$transaction([
      this.prisma.reminderLog.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        include: {
          assignment: { include: { employee: true, campaign: true } },
        },
        orderBy: { sentAt: 'desc' },
      }),
      this.prisma.reminderLog.count({ where }),
    ]);

    return { items, total, page, limit };
  }

  async sendReminderForAssignment(assignmentId: string, actor: JwtAuthUser) {
    const assignment = await this.prisma.assessmentAssignment.findFirst({
      where: {
        id: assignmentId,
        deletedAt: null,
      },
      include: {
        employee: true,
        campaign: true,
      },
    });

    if (!assignment) {
      throw new Error('Assignment not found');
    }

    const remindableStatuses = new Set<AssignmentStatus>([
      AssignmentStatus.PENDING,
      AssignmentStatus.DRAFT,
      AssignmentStatus.REOPENED,
    ]);

    if (
      !assignment.isEnabled ||
      !remindableStatuses.has(assignment.status)
    ) {
      return null;
    }

    const todayKey = new Date().toISOString().slice(0, 10);
    const existingToday = await this.prisma.reminderLog.findFirst({
      where: {
        assignmentId,
        deliveryStatus: DeliveryStatus.SENT,
        deletedAt: null,
        createdAt: {
          gte: new Date(`${todayKey}T00:00:00.000Z`),
          lt: new Date(`${todayKey}T23:59:59.999Z`),
        },
      },
    });

    if (existingToday) {
      return existingToday;
    }

    const log = await this.prisma.reminderLog.create({
      data: {
        assignmentId,
        channel: ReminderChannel.EMAIL,
        recipient: assignment.employee.email,
        subject: `Reminder: ${assignment.campaign.title}`,
        body: `Please complete your assessment for ${assignment.campaign.title}.`,
        sentAt: new Date(),
        deliveryStatus: DeliveryStatus.SENT,
        metaJson: {
          generatedBy: 'system',
          assignmentStatus: assignment.status,
        } as Prisma.InputJsonValue,
        createdBy: actor.sub,
        updatedBy: actor.sub,
      },
    });

    await this.auditService.log({
      module: 'reminders',
      action: AuditAction.UPDATE,
      entityType: AuditEntityType.ASSIGNMENT,
      entityId: assignmentId,
      remarks: 'Reminder triggered',
      newValues: { reminderLogId: log.id } as Prisma.InputJsonValue,
      createdBy: actor.sub,
      updatedBy: actor.sub,
    });

    return log;
  }

  async runPendingReminders(actor: JwtAuthUser, assessmentType?: 'MONTHLY' | 'ANNUAL') {
    const assignments = await this.prisma.assessmentAssignment.findMany({
      where: {
        deletedAt: null,
        isEnabled: true,
        status: {
          in: [AssignmentStatus.PENDING, AssignmentStatus.DRAFT, AssignmentStatus.REOPENED],
        },
        campaign: {
          deletedAt: null,
          ...(assessmentType ? { assessmentType } : {}),
        },
      },
      select: { id: true },
    });

    const sent = [];
    for (const assignment of assignments) {
      const log = await this.sendReminderForAssignment(assignment.id, actor);
      if (log) sent.push(log.id);
    }

    return {
      processed: assignments.length,
      sent: sent.length,
      reminderLogIds: sent,
    };
  }
}
