import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { CouponType, Prisma, UserRole } from '@prisma/client';
import { PrismaService } from '../database/prisma.service';

type RequestUser = {
  sub: string;
  role: UserRole;
};

type UpsertCouponInput = {
  code: string;
  type: CouponType;
  value: number;
  minOrderValue?: number | null;
  usageLimit?: number | null;
  perUserLimit?: number | null;
  validFrom: string;
  validUntil: string;
  isActive?: boolean;
};

type UpsertPromotionInput = {
  title: string;
  description?: string | null;
  discountType: CouponType;
  discountValue: number;
  startsAt: string;
  endsAt: string;
  isActive?: boolean;
};

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

  async listCoupons(user: RequestUser) {
    const restaurantId = await this.requireRestaurantId(user);
    return this.prisma.coupon.findMany({
      where: { restaurantId },
      orderBy: [{ isActive: 'desc' }, { validUntil: 'asc' }, { code: 'asc' }],
    });
  }

  async createCoupon(user: RequestUser, body: UpsertCouponInput) {
    const restaurantId = await this.requireRestaurantId(user);
    return this.prisma.coupon.create({
      data: {
        restaurantId,
        code: body.code.trim().toUpperCase(),
        type: body.type,
        value: new Prisma.Decimal(body.value),
        minOrderValue: body.minOrderValue === undefined || body.minOrderValue === null ? null : new Prisma.Decimal(body.minOrderValue),
        usageLimit: body.usageLimit ?? null,
        perUserLimit: body.perUserLimit ?? null,
        validFrom: new Date(body.validFrom),
        validUntil: new Date(body.validUntil),
        isActive: body.isActive ?? true,
      },
    });
  }

  async updateCoupon(user: RequestUser, id: string, body: Partial<UpsertCouponInput>) {
    const restaurantId = await this.requireRestaurantId(user);
    const current = await this.prisma.coupon.findUnique({ where: { id } });
    if (!current || current.restaurantId !== restaurantId) {
      throw new NotFoundException('Cupom nao encontrado.');
    }

    return this.prisma.coupon.update({
      where: { id },
      data: {
        code: body.code ? body.code.trim().toUpperCase() : undefined,
        type: body.type,
        value: body.value === undefined ? undefined : new Prisma.Decimal(body.value),
        minOrderValue:
          body.minOrderValue === undefined
            ? undefined
            : body.minOrderValue === null
              ? null
              : new Prisma.Decimal(body.minOrderValue),
        usageLimit: body.usageLimit === undefined ? undefined : body.usageLimit,
        perUserLimit: body.perUserLimit === undefined ? undefined : body.perUserLimit,
        validFrom: body.validFrom ? new Date(body.validFrom) : undefined,
        validUntil: body.validUntil ? new Date(body.validUntil) : undefined,
        isActive: body.isActive,
      },
    });
  }

  async archiveCoupon(user: RequestUser, id: string) {
    const restaurantId = await this.requireRestaurantId(user);
    const current = await this.prisma.coupon.findUnique({ where: { id } });
    if (!current || current.restaurantId !== restaurantId) {
      throw new NotFoundException('Cupom nao encontrado.');
    }
    return this.prisma.coupon.update({
      where: { id },
      data: { isActive: false },
    });
  }

  async listPromotions(user: RequestUser) {
    const restaurantId = await this.requireRestaurantId(user);
    return this.prisma.promotion.findMany({
      where: { restaurantId },
      orderBy: [{ isActive: 'desc' }, { endsAt: 'asc' }, { title: 'asc' }],
    });
  }

  async createPromotion(user: RequestUser, body: UpsertPromotionInput) {
    return this.prisma.promotion.create({
      data: {
        restaurantId: await this.requireRestaurantId(user),
        title: body.title,
        description: body.description,
        discountType: body.discountType,
        discountValue: new Prisma.Decimal(body.discountValue),
        startsAt: new Date(body.startsAt),
        endsAt: new Date(body.endsAt),
        isActive: body.isActive ?? true,
      },
    });
  }

  async updatePromotion(user: RequestUser, id: string, body: Partial<UpsertPromotionInput>) {
    const restaurantId = await this.requireRestaurantId(user);
    const current = await this.prisma.promotion.findUnique({ where: { id } });
    if (!current || current.restaurantId !== restaurantId) {
      throw new NotFoundException('Promocao nao encontrada.');
    }

    return this.prisma.promotion.update({
      where: { id },
      data: {
        title: body.title,
        description: body.description,
        discountType: body.discountType,
        discountValue: body.discountValue === undefined ? undefined : new Prisma.Decimal(body.discountValue),
        startsAt: body.startsAt ? new Date(body.startsAt) : undefined,
        endsAt: body.endsAt ? new Date(body.endsAt) : undefined,
        isActive: body.isActive,
      },
    });
  }

  async archivePromotion(user: RequestUser, id: string) {
    const restaurantId = await this.requireRestaurantId(user);
    const current = await this.prisma.promotion.findUnique({ where: { id } });
    if (!current || current.restaurantId !== restaurantId) {
      throw new NotFoundException('Promocao nao encontrada.');
    }
    return this.prisma.promotion.update({
      where: { id },
      data: { isActive: false },
    });
  }

  private async requireRestaurantId(user: RequestUser) {
    if (user.role !== UserRole.RESTAURANT) {
      throw new ForbiddenException('Acesso restrito ao restaurante.');
    }

    const restaurant = await this.prisma.restaurant.findUnique({
      where: { userId: user.sub },
      select: { id: true },
    });
    if (!restaurant) {
      throw new NotFoundException('Loja autenticada nao encontrada.');
    }
    return restaurant.id;
  }
}
