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

interface UpsertPlanInput {
  code: string;
  name: string;
  tier?: PlanTier;
  fixedPrice: number;
  pricePerLot: number;
  currency?: string;
  features?: Record<string, boolean>;
}

interface SubscribeInput {
  tenantId: string;
  planCode: string;
}

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

  // ---------- Plans (SUPERADMIN) ----------

  async upsertPlan(user: AuthenticatedUser, dto: UpsertPlanInput) {
    if (!user.isSuperAdmin) throw new ForbiddenException();
    return this.prisma.plan.upsert({
      where: { code: dto.code },
      update: {
        name: dto.name,
        tier: dto.tier ?? PlanTier.BASE,
        fixedPrice: new Prisma.Decimal(dto.fixedPrice),
        pricePerLot: new Prisma.Decimal(dto.pricePerLot),
        currency: dto.currency ?? 'MAD',
        features: dto.features ?? {},
      },
      create: {
        code: dto.code,
        name: dto.name,
        tier: dto.tier ?? PlanTier.BASE,
        fixedPrice: new Prisma.Decimal(dto.fixedPrice),
        pricePerLot: new Prisma.Decimal(dto.pricePerLot),
        currency: dto.currency ?? 'MAD',
        features: dto.features ?? {},
      },
    });
  }

  listPlans(user: AuthenticatedUser) {
    return this.prisma.plan.findMany({
      where: user.isSuperAdmin ? {} : { isActive: true },
      orderBy: { fixedPrice: 'asc' },
    });
  }

  // ---------- Feature toggles ----------

  async toggleFeature(
    user: AuthenticatedUser,
    tenantId: string,
    featureKey: string,
    enabled: boolean,
  ) {
    if (!user.isSuperAdmin) throw new ForbiddenException();
    return this.prisma.tenantFeatureToggle.upsert({
      where: { tenantId_featureKey: { tenantId, featureKey } },
      update: { enabled, activatedAt: enabled ? new Date() : null },
      create: { tenantId, featureKey, enabled, activatedAt: enabled ? new Date() : null },
    });
  }

  listFeatures(user: AuthenticatedUser, tenantId: string) {
    if (!user.isSuperAdmin && user.tenantId !== tenantId) throw new ForbiddenException();
    return this.prisma.tenantFeatureToggle.findMany({ where: { tenantId } });
  }

  // ---------- Subscription ----------

  async subscribe(user: AuthenticatedUser, dto: SubscribeInput) {
    if (!user.isSuperAdmin) throw new ForbiddenException();
    const plan = await this.prisma.plan.findUnique({ where: { code: dto.planCode } });
    if (!plan) throw new NotFoundException('Plan not found');
    const complexes = await this.prisma.complex.findMany({
      where: { tenantId: dto.tenantId },
      select: { id: true },
    });
    const lotsCount = await this.prisma.lot.count({
      where: { complexId: { in: complexes.map((c) => c.id) } },
    });
    // Close previous active
    await this.prisma.tenantSubscription.updateMany({
      where: { tenantId: dto.tenantId, status: 'ACTIVE' },
      data: { status: 'CANCELLED', endedAt: new Date() },
    });
    return this.prisma.tenantSubscription.create({
      data: {
        tenantId: dto.tenantId,
        planCode: plan.code,
        lotsCount,
        fixedPrice: plan.fixedPrice,
        pricePerLot: plan.pricePerLot,
        currency: plan.currency,
      },
    });
  }

  // ---------- SaaS invoice generation ----------

  async generateInvoice(
    user: AuthenticatedUser,
    tenantId: string,
    opts: { periodStart: Date; periodEnd: Date },
  ) {
    if (!user.isSuperAdmin) throw new ForbiddenException();
    const sub = await this.prisma.tenantSubscription.findFirst({
      where: { tenantId, status: 'ACTIVE' },
    });
    if (!sub) throw new BadRequestException('No active subscription');

    const complexes = await this.prisma.complex.findMany({
      where: { tenantId },
      select: { id: true },
    });
    const lotsCount = await this.prisma.lot.count({
      where: { complexId: { in: complexes.map((c) => c.id) } },
    });
    const fixed = new Prisma.Decimal(sub.fixedPrice);
    const perLot = new Prisma.Decimal(sub.pricePerLot);
    const amount = fixed.plus(perLot.mul(lotsCount));

    const dueDate = new Date(opts.periodEnd);
    dueDate.setDate(dueDate.getDate() + 30);

    return this.prisma.saasInvoice.create({
      data: {
        tenantId,
        amount,
        currency: sub.currency,
        periodStart: opts.periodStart,
        periodEnd: opts.periodEnd,
        lotsCount,
        dueDate,
      },
    });
  }

  listInvoices(user: AuthenticatedUser, tenantId?: string) {
    if (user.isSuperAdmin) {
      return this.prisma.saasInvoice.findMany({
        where: tenantId ? { tenantId } : {},
        orderBy: { createdAt: 'desc' },
        include: { tenant: { select: { id: true, companyName: true } } },
      });
    }
    if (!user.tenantId) return [];
    return this.prisma.saasInvoice.findMany({
      where: { tenantId: user.tenantId },
      orderBy: { createdAt: 'desc' },
    });
  }

  async markInvoicePaid(user: AuthenticatedUser, invoiceId: string) {
    if (!user.isSuperAdmin) throw new ForbiddenException();
    return this.prisma.saasInvoice.update({
      where: { id: invoiceId },
      data: { status: 'PAID', paidAt: new Date() },
    });
  }
}
