import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import { CreatePromotionDto } from './promos.dto';
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';

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

  getActive() {
    return this.prisma.promotion.findMany({
      where: { active: true, validUntil: { gte: new Date() } },
      orderBy: { createdAt: 'desc' },
    });
  }

  async getAll(filters: { search?: string; active?: boolean; page?: number; pageSize?: number }) {
    const { search, active, page = 1, pageSize = 10 } = filters;
    const skip = (page - 1) * pageSize;

    const where: any = {};
    if (search) {
      where.OR = [
        { code: { contains: search, mode: 'insensitive' } },
        { title: { contains: search, mode: 'insensitive' } },
      ];
    }
    if (active !== undefined) {
      where.active = active;
    }

    const [items, total] = await Promise.all([
      this.prisma.promotion.findMany({
        where,
        skip,
        take: pageSize,
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.promotion.count({ where }),
    ]);

    return { items: this.formatItems(items), total, page, pageSize };
  }

  async getById(id: string) {
    const promo = await this.prisma.promotion.findUnique({ where: { id } });
    if (!promo) throw new NotFoundException('Promo not found');
    return this.formatItem(promo);
  }

  async validate(code: string) {
    const promo = await this.prisma.promotion.findUnique({ where: { code } });
    if (!promo || !promo.active || promo.validUntil < new Date())
      return { applicable: false, message: 'Promo code invalid or expired' };
    return {
      code: promo.code,
      percentOff: promo.percentOff,
      amountOffMinor: promo.amountOffMinor,
      validUntil: promo.validUntil,
      applicable: true,
      message: promo.percentOff
        ? `${promo.percentOff}% off`
        : `ETB ${((promo.amountOffMinor ?? 0) / 100).toFixed(2)} off`,
    };
  }

  async create(dto: CreatePromotionDto & { discountType?: string; discountValue?: number }) {
    try {
      // Map frontend fields to database fields
      let percentOff: number | undefined;
      let amountOffMinor: number | undefined;

      if (dto.discountType && dto.discountValue !== undefined) {
        if (dto.discountType === 'PERCENTAGE') {
          percentOff = dto.discountValue;
        } else if (dto.discountType === 'FIXED') {
          amountOffMinor = dto.discountValue;
        }
      } else {
        // Fallback to direct fields
        percentOff = dto.percentOff;
        amountOffMinor = dto.amountOffMinor;
      }

      const promo = await this.prisma.promotion.create({
        data: {
          code: dto.code,
          title: dto.title,
          subtitle: dto.subtitle,
          percentOff,
          amountOffMinor,
          validUntil: new Date(dto.validUntil),
          ctaLabel: dto.ctaLabel,
          deepLink: dto.deepLink,
          active: dto.active ?? true,
        },
      });
      return this.formatItem(promo);
    } catch (error) {
      if (error instanceof PrismaClientKnownRequestError) {
        if (error.code === 'P2002') {
          const field = (error.meta?.target as string[])?.[0];
          throw new BadRequestException(
            `A promo code with this ${field} already exists. Please use a different ${field}.`,
          );
        }
      }
      throw error;
    }
  }

  async update(id: string, dto: Partial<CreatePromotionDto> & { discountType?: string; discountValue?: number }) {
    const promo = await this.prisma.promotion.findUnique({ where: { id } });
    if (!promo) throw new NotFoundException('Promo not found');

    const updateData: any = {};
    
    // Map frontend fields to database fields
    if (dto.discountType && dto.discountValue !== undefined) {
      // Clear existing discount fields
      updateData.percentOff = null;
      updateData.amountOffMinor = null;
      
      if (dto.discountType === 'PERCENTAGE') {
        updateData.percentOff = dto.discountValue;
      } else if (dto.discountType === 'FIXED') {
        updateData.amountOffMinor = dto.discountValue;
      }
    } else {
      // Only include fields that are explicitly provided
      if (dto.percentOff !== undefined) updateData.percentOff = dto.percentOff;
      if (dto.amountOffMinor !== undefined) updateData.amountOffMinor = dto.amountOffMinor;
    }
    
    if (dto.title !== undefined) updateData.title = dto.title;
    if (dto.subtitle !== undefined) updateData.subtitle = dto.subtitle;
    if (dto.ctaLabel !== undefined) updateData.ctaLabel = dto.ctaLabel;
    if (dto.deepLink !== undefined) updateData.deepLink = dto.deepLink;
    if (dto.active !== undefined) updateData.active = dto.active;
    if (dto.validUntil !== undefined) updateData.validUntil = new Date(dto.validUntil);

    // Don't allow updating code - it's immutable after creation
    
    try {
      const updated = await this.prisma.promotion.update({
        where: { id },
        data: updateData,
      });
      return this.formatItem(updated);
    } catch (error) {
      if (error instanceof PrismaClientKnownRequestError && error.code === 'P2002') {
        const field = (error.meta?.target as string[])?.[0];
        throw new BadRequestException(
          `A promo code with this ${field} already exists. Please use a different ${field}.`,
        );
      }
      throw error;
    }
  }

  async delete(id: string) {
    const promo = await this.prisma.promotion.findUnique({ where: { id } });
    if (!promo) throw new NotFoundException('Promo not found');
    return this.prisma.promotion.delete({ where: { id } });
  }

  private formatItem(promo: any) {
    return {
      id: promo.id,
      code: promo.code,
      title: promo.title,
      discountType: promo.percentOff ? 'PERCENTAGE' : 'FIXED',
      discountValue: promo.percentOff || promo.amountOffMinor || 0,
      maxDiscount: undefined,
      minBookingAmount: undefined,
      maxUsagePerUser: undefined,
      totalUsageLimit: undefined,
      usageCount: 0,
      validFrom: promo.createdAt,
      validUntil: promo.validUntil,
      isActive: promo.active,
      createdAt: promo.createdAt,
      updatedAt: promo.createdAt,
    };
  }

  private formatItems(promos: any[]) {
    return promos.map((promo) => this.formatItem(promo));
  }
}
