import {
  Injectable,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateLicenseDto } from './dto/create-license.dto';
import { UpdateLicenseDto } from './dto/update-license.dto';
import { RenewLicenseDto } from './dto/renew-license.dto';
import { FindLicensesDto } from './dto/find-licenses.dto';
import { Prisma } from '@prisma/client';

export interface PaginatedResult<T> {
  data: T[];
  nextCursor: string | null;
  hasMore: boolean;
}

const GRACE_PERIOD_DAYS = 3;

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

  async findAll(params: FindLicensesDto): Promise<PaginatedResult<any>> {
    const { cursor, limit = 20, type, isActive } = params;

    const where: Prisma.LicenseWhereInput = {
      ...(type ? { type } : {}),
      ...(isActive !== undefined ? { isActive } : {}),
    };

    const items = await this.prisma.license.findMany({
      where,
      take: limit + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { createdAt: 'desc' },
      include: { club: { select: { id: true, name: true, slug: true } } },
    });

    const hasMore = items.length > limit;
    const data = hasMore ? items.slice(0, -1) : items;

    return {
      data,
      nextCursor: hasMore ? data[data.length - 1].id : null,
      hasMore,
    };
  }

  async findById(id: string) {
    const license = await this.prisma.license.findUnique({
      where: { id },
      include: { club: { select: { id: true, name: true, slug: true } } },
    });

    if (!license) {
      throw new NotFoundException(`License with id ${id} not found`);
    }
    return license;
  }

  async findByClubId(clubId: string) {
    const license = await this.prisma.license.findUnique({
      where: { clubId },
      include: { club: { select: { id: true, name: true, slug: true } } },
    });

    if (!license) {
      throw new NotFoundException(`License for club ${clubId} not found`);
    }
    return this.enrichWithStatus(license);
  }

  async create(dto: CreateLicenseDto) {
    const existing = await this.prisma.license.findUnique({
      where: { clubId: dto.clubId },
    });
    if (existing) {
      throw new BadRequestException(
        `Club ${dto.clubId} already has a license. Use PATCH to update or POST :id/renew to renew.`,
      );
    }

    const clubCount = await this.prisma.club.count({
      where: { id: dto.clubId },
    });
    if (clubCount === 0) {
      throw new NotFoundException(`Club with id ${dto.clubId} not found`);
    }

    return this.prisma.license.create({
      data: {
        clubId: dto.clubId,
        type: dto.type,
        startDate: new Date(dto.startDate),
        endDate: new Date(dto.endDate),
        maxUsers: dto.maxUsers,
        maxClients: dto.maxClients,
        isActive: true,
      },
      include: { club: { select: { id: true, name: true, slug: true } } },
    });
  }

  async update(id: string, dto: UpdateLicenseDto) {
    await this.ensureExists(id);

    return this.prisma.license.update({
      where: { id },
      data: {
        ...(dto.type !== undefined ? { type: dto.type } : {}),
        ...(dto.endDate !== undefined
          ? { endDate: new Date(dto.endDate) }
          : {}),
        ...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
        ...(dto.maxUsers !== undefined ? { maxUsers: dto.maxUsers } : {}),
        ...(dto.maxClients !== undefined
          ? { maxClients: dto.maxClients }
          : {}),
      },
      include: { club: { select: { id: true, name: true, slug: true } } },
    });
  }

  async renew(id: string, dto: RenewLicenseDto) {
    const license = await this.prisma.license.findUnique({ where: { id } });
    if (!license) {
      throw new NotFoundException(`License with id ${id} not found`);
    }

    const newEndDate = new Date(dto.newEndDate);
    if (newEndDate <= new Date()) {
      throw new BadRequestException('New end date must be in the future');
    }

    return this.prisma.license.update({
      where: { id },
      data: {
        endDate: newEndDate,
        isActive: true,
        gracePeriodEnd: null,
        ...(dto.type ? { type: dto.type } : {}),
      },
      include: { club: { select: { id: true, name: true, slug: true } } },
    });
  }

  private enrichWithStatus(license: any) {
    const now = new Date();
    const endDate = new Date(license.endDate);
    const daysUntilExpiry = Math.ceil(
      (endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),
    );

    let status: string;
    if (!license.isActive) {
      status = 'BLOCKED';
    } else if (daysUntilExpiry > 30) {
      status = 'ACTIVE';
    } else if (daysUntilExpiry > 7) {
      status = 'EXPIRING_SOON';
    } else if (daysUntilExpiry > 0) {
      status = 'EXPIRING_CRITICAL';
    } else if (daysUntilExpiry > -GRACE_PERIOD_DAYS) {
      status = 'GRACE_PERIOD';
    } else if (daysUntilExpiry > -14) {
      status = 'READ_ONLY';
    } else {
      status = 'BLOCKED';
    }

    return { ...license, status, daysUntilExpiry };
  }

  private async ensureExists(id: string): Promise<void> {
    const count = await this.prisma.license.count({ where: { id } });
    if (count === 0) {
      throw new NotFoundException(`License with id ${id} not found`);
    }
  }
}
