import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../database/prisma.service';
import { AppCategory } from '@prisma/client';

export interface UpsertAppLimitDto {
  packageName: string;
  appName: string;
  category?: AppCategory;
  dailyLimitMinutes?: number | null;
  isBlocked?: boolean;
  isEssential?: boolean;
}

export interface UpsertCategoryLimitDto {
  category: AppCategory;
  dailyLimitMinutes?: number | null;
  isBlocked?: boolean;
}

export interface UpsertScheduleDto {
  name: string;
  startTime: string;
  endTime: string;
  daysOfWeek: number[];
  blockedCategories?: AppCategory[];
  allowedCategories?: AppCategory[];
  isActive?: boolean;
}

export interface UpsertRoutineDto {
  name: string;
  startTime: string;
  endTime: string;
  daysOfWeek: number[];
  warnMinutesBefore?: number[];
  blockedCategories?: AppCategory[];
  isActive?: boolean;
}

@Injectable()
export class PoliciesService {
  constructor(private readonly prisma: PrismaService) {}

  async getPolicy(childId: string) {
    const policy = await this.prisma.policy.findUnique({
      where: { childId },
      include: {
        appLimits: true,
        catLimits: true,
        schedules: true,
        routineRules: true,
      },
    });
    if (!policy) throw new NotFoundException('Política não encontrada');
    return policy;
  }

  /** Retorna a policy serializada para sync no dispositivo */
  async getPolicyForDevice(childId: string) {
    const policy = await this.getPolicy(childId);
    const wallet = await this.prisma.timeWallet.findUnique({ where: { childId } });

    return {
      policyVersion: policy.version,
      updatedAt: policy.updatedAt,
      wallet: wallet
        ? {
            balanceMinutes: wallet.balanceMinutes,
            dailyMaxMinutes: wallet.dailyMaxMinutes,
            usedMinutesToday: wallet.usedMinutesToday,
          }
        : null,
      appLimits: policy.appLimits,
      categoryLimits: policy.catLimits,
      schedules: policy.schedules,
      routineRules: policy.routineRules,
    };
  }

  async upsertAppLimit(childId: string, dto: UpsertAppLimitDto, guardianId: string) {
    const policy = await this.ensurePolicy(childId);

    const limit = await this.prisma.appLimit.upsert({
      where: { policyId_packageName: { policyId: policy.id, packageName: dto.packageName } },
      create: { policyId: policy.id, ...dto },
      update: { ...dto },
    });

    await this.bumpPolicyVersion(policy.id, guardianId);
    return limit;
  }

  async removeAppLimit(childId: string, packageName: string, guardianId: string) {
    const policy = await this.ensurePolicy(childId);
    await this.prisma.appLimit.deleteMany({
      where: { policyId: policy.id, packageName },
    });
    await this.bumpPolicyVersion(policy.id, guardianId);
  }

  async upsertCategoryLimit(childId: string, dto: UpsertCategoryLimitDto, guardianId: string) {
    const policy = await this.ensurePolicy(childId);

    const limit = await this.prisma.categoryLimit.upsert({
      where: { policyId_category: { policyId: policy.id, category: dto.category } },
      create: { policyId: policy.id, ...dto },
      update: { ...dto },
    });

    await this.bumpPolicyVersion(policy.id, guardianId);
    return limit;
  }

  async upsertSchedule(childId: string, dto: UpsertScheduleDto, guardianId: string) {
    const policy = await this.ensurePolicy(childId);

    const schedule = await this.prisma.schedule.create({
      data: { policyId: policy.id, ...dto },
    });

    await this.bumpPolicyVersion(policy.id, guardianId);
    return schedule;
  }

  async updateSchedule(scheduleId: string, dto: Partial<UpsertScheduleDto>, guardianId: string) {
    const schedule = await this.prisma.schedule.update({
      where: { id: scheduleId },
      data: dto,
    });
    await this.bumpPolicyVersion(schedule.policyId, guardianId);
    return schedule;
  }

  async deleteSchedule(scheduleId: string, guardianId: string) {
    const schedule = await this.prisma.schedule.findUnique({ where: { id: scheduleId } });
    if (!schedule) throw new NotFoundException();
    await this.prisma.schedule.delete({ where: { id: scheduleId } });
    await this.bumpPolicyVersion(schedule.policyId, guardianId);
  }

  async upsertRoutine(childId: string, dto: UpsertRoutineDto, guardianId: string) {
    const policy = await this.ensurePolicy(childId);

    const routine = await this.prisma.routineRule.create({
      data: { policyId: policy.id, ...dto },
    });

    await this.bumpPolicyVersion(policy.id, guardianId);
    return routine;
  }

  async updateRoutine(routineId: string, dto: Partial<UpsertRoutineDto>, guardianId: string) {
    const routine = await this.prisma.routineRule.update({
      where: { id: routineId },
      data: dto,
    });
    await this.bumpPolicyVersion(routine.policyId, guardianId);
    return routine;
  }

  private async ensurePolicy(childId: string) {
    let policy = await this.prisma.policy.findUnique({ where: { childId } });
    if (!policy) {
      policy = await this.prisma.policy.create({ data: { childId } });
    }
    return policy;
  }

  private async bumpPolicyVersion(policyId: string, guardianId: string) {
    await this.prisma.policy.update({
      where: { id: policyId },
      data: { version: { increment: 1 }, updatedBy: guardianId },
    });
  }
}
