import { Injectable } from '@nestjs/common';
import { AssignmentStatus, CronJobExecutionStatus, Prisma } from '@prisma/client';
import { CampaignsService } from '../campaigns/campaigns.service';
import { PrismaService } from '../prisma/prisma.service';
import { RemindersService } from '../reminders/reminders.service';

const SYSTEM_ACTOR = {
  sub: 'SYSTEM',
  email: 'system@aechr.local',
  employeeCode: 'SYSTEM',
  name: 'SYSTEM',
  roleId: 'SYSTEM',
  departmentId: 'SYSTEM',
  designation: 'System',
  permissions: [],
};

@Injectable()
export class InternalCronService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly campaignsService: CampaignsService,
    private readonly remindersService: RemindersService,
  ) {}

  async runMonthlyGenerate(requestIp?: string | null) {
    return this.executeJob('monthly_generate', requestIp, async () => {
      const campaign = await this.campaignsService.generateMonthly({}, SYSTEM_ACTOR);
      return { campaignId: campaign.id };
    });
  }

  async runRecurringGenerate(requestIp?: string | null) {
    return this.executeJob('recurring_generate', requestIp, async () => {
      return this.campaignsService.runRecurringGeneration(SYSTEM_ACTOR);
    });
  }

  async runMonthlyReminders(requestIp?: string | null) {
    return this.executeJob('monthly_reminders', requestIp, async () => {
      return this.remindersService.runPendingReminders(SYSTEM_ACTOR, 'MONTHLY');
    });
  }

  async runAnnualReminders(requestIp?: string | null) {
    return this.executeJob('annual_reminders', requestIp, async () => {
      return this.remindersService.runPendingReminders(SYSTEM_ACTOR, 'ANNUAL');
    });
  }

  async lockExpiredAssignments(requestIp?: string | null) {
    return this.executeJob('assignments_lock_expired', requestIp, async () => {
      const now = new Date();
      const result = await this.prisma.assessmentAssignment.updateMany({
        where: {
          deletedAt: null,
          status: {
            in: [AssignmentStatus.PENDING, AssignmentStatus.DRAFT, AssignmentStatus.REOPENED],
          },
          campaign: {
            endDate: { lt: now },
          },
        },
        data: {
          status: AssignmentStatus.LOCKED,
          updatedBy: SYSTEM_ACTOR.sub,
        },
      });

      return { lockedCount: result.count };
    });
  }

  private async executeJob(
    jobName: string,
    requestIp: string | null | undefined,
    handler: () => Promise<Record<string, unknown>>,
  ) {
    const running = await this.prisma.cronJobLog.findFirst({
      where: {
        jobName,
        status: CronJobExecutionStatus.RUNNING,
        deletedAt: null,
      },
      orderBy: { startedAt: 'desc' },
    });

    if (running) {
      return {
        skipped: true,
        reason: 'Job already running',
        logId: running.id,
      };
    }

    const log = await this.prisma.cronJobLog.create({
      data: {
        jobName,
        startedAt: new Date(),
        status: CronJobExecutionStatus.RUNNING,
        requestIp: requestIp ?? null,
        createdBy: SYSTEM_ACTOR.sub,
        updatedBy: SYSTEM_ACTOR.sub,
      },
    });

    try {
      const summary = await handler();
      await this.prisma.cronJobLog.update({
        where: { id: log.id },
        data: {
          endedAt: new Date(),
          status: CronJobExecutionStatus.SUCCESS,
          responseSummaryJson: summary as Prisma.InputJsonValue,
          updatedBy: SYSTEM_ACTOR.sub,
        },
      });
      return {
        skipped: false,
        logId: log.id,
        ...summary,
      };
    } catch (error) {
      await this.prisma.cronJobLog.update({
        where: { id: log.id },
        data: {
          endedAt: new Date(),
          status: CronJobExecutionStatus.FAILED,
          errorMessage: error instanceof Error ? error.message : 'Unknown error',
          updatedBy: SYSTEM_ACTOR.sub,
        },
      });
      throw error;
    }
  }
}
