import {
  BadRequestException,
  ForbiddenException,
  Injectable,
  Logger,
  NotFoundException,
} from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { MaintenanceStatus, Prisma } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import { AuthenticatedUser } from '../../common/decorators/current-user.decorator';

interface CreatePlanInput {
  complexId: string;
  title: string;
  equipment: string;
  frequencyDays: number;
  nextDueDate: Date;
  providerId?: string;
}

interface CompleteEventInput {
  eventId: string;
  reportUrl?: string;
  notes?: string;
}

@Injectable()
export class MaintenanceService {
  private readonly logger = new Logger(MaintenanceService.name);

  constructor(private readonly prisma: PrismaService) {}

  private async assertComplexAccess(user: AuthenticatedUser, complexId: string) {
    if (user.isSuperAdmin) return;
    const c = await this.prisma.complex.findFirst({
      where: { id: complexId, tenantId: user.tenantId ?? undefined },
    });
    if (!c) throw new ForbiddenException();
  }

  async createPlan(user: AuthenticatedUser, dto: CreatePlanInput) {
    await this.assertComplexAccess(user, dto.complexId);
    if (dto.frequencyDays <= 0) {
      throw new BadRequestException('frequencyDays must be positive');
    }
    const plan = await this.prisma.maintenancePlan.create({
      data: {
        complexId: dto.complexId,
        title: dto.title,
        equipment: dto.equipment,
        frequencyDays: dto.frequencyDays,
        nextDueDate: dto.nextDueDate,
        providerId: dto.providerId,
      },
    });
    // Seed first event
    await this.prisma.maintenanceEvent.create({
      data: { planId: plan.id, dueDate: dto.nextDueDate },
    });
    return plan;
  }

  async listPlans(user: AuthenticatedUser, complexId?: string) {
    return this.prisma.maintenancePlan.findMany({
      where: {
        ...(complexId && { complexId }),
        ...(!user.isSuperAdmin && { complex: { tenantId: user.tenantId ?? undefined } }),
      },
      include: { provider: true, events: { orderBy: { dueDate: 'desc' }, take: 5 } },
      orderBy: { nextDueDate: 'asc' },
    });
  }

  async listEvents(user: AuthenticatedUser, complexId: string) {
    await this.assertComplexAccess(user, complexId);
    return this.prisma.maintenanceEvent.findMany({
      where: { plan: { complexId } },
      include: { plan: true },
      orderBy: { dueDate: 'desc' },
      take: 200,
    });
  }

  async completeEvent(user: AuthenticatedUser, dto: CompleteEventInput) {
    const evt = await this.prisma.maintenanceEvent.findUnique({
      where: { id: dto.eventId },
      include: { plan: true },
    });
    if (!evt) throw new NotFoundException();
    await this.assertComplexAccess(user, evt.plan.complexId);
    const now = new Date();
    const nextDue = new Date(now.getTime() + evt.plan.frequencyDays * 86400000);

    return this.prisma.$transaction(async (tx) => {
      const done = await tx.maintenanceEvent.update({
        where: { id: evt.id },
        data: {
          status: MaintenanceStatus.DONE,
          executedAt: now,
          reportUrl: dto.reportUrl,
          notes: dto.notes,
        },
      });
      await tx.maintenancePlan.update({
        where: { id: evt.planId },
        data: { nextDueDate: nextDue },
      });
      // Seed next event
      const next = await tx.maintenanceEvent.create({
        data: { planId: evt.planId, dueDate: nextDue },
      });
      return { completed: done, next };
    });
  }

  /**
   * Daily cron — mark DUE any events whose dueDate has passed and are still SCHEDULED.
   * Runs 07:15 every morning.
   */
  @Cron('15 7 * * *')
  async markDueEvents() {
    const now = new Date();
    const res = await this.prisma.maintenanceEvent.updateMany({
      where: { status: MaintenanceStatus.SCHEDULED, dueDate: { lte: now } },
      data: { status: MaintenanceStatus.DUE },
    });
    if (res.count > 0) {
      this.logger.log(`Marked ${res.count} maintenance events DUE`);
    }
    return res;
  }

  /** Manual trigger (admin) */
  triggerNow() {
    return this.markDueEvents();
  }
}
