import { Injectable, Logger, ConflictException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateClubDto, LicenseType } from './dto/create-club.dto';

export interface Club {
  id: string;
  name: string;
  slug: string;
  address: string | null;
  phone: string | null;
  email: string | null;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

/**
 * Default modules enabled per license type.
 * STARTER gets the core set; STANDARD and PREMIUM unlock more.
 */
const DEFAULT_MODULES: Record<LicenseType, string[]> = {
  [LicenseType.STARTER]: [
    'core',
    'sales',
    'coordinator',
    'push',
    'web_reception',
  ],
  [LicenseType.STANDARD]: [
    'core',
    'sales',
    'coordinator',
    'push',
    'web_reception',
    'web_reports',
    'sip',
    'webhooks',
    'club_admin',
    '1c_sync',
  ],
  [LicenseType.PREMIUM]: [
    'core',
    'sales',
    'coordinator',
    'push',
    'web_reception',
    'web_reports',
    'sip',
    'sip_recording',
    'webhooks',
    'club_admin',
    '1c_sync',
  ],
};

/**
 * ProvisioningService orchestrates the creation and deactivation
 * of club environments. In the MVP (RLS strategy), this means
 * inserting rows into clubs, licenses, and club_modules.
 */
@Injectable()
export class ProvisioningService {
  private readonly logger = new Logger(ProvisioningService.name);

  constructor(private readonly prisma: PrismaService) {}

  /**
   * Provision a new club: create Club + License + default ClubModules
   * inside a single database transaction for atomicity.
   */
  async provisionClub(dto: CreateClubDto): Promise<Club> {
    const licenseType = dto.licenseType ?? LicenseType.STARTER;
    const maxUsers = dto.maxUsers ?? 5;
    const maxClients = dto.maxClients ?? 100;

    // Check slug uniqueness
    const existing = await this.prisma.$queryRawUnsafe<{ id: string }[]>(
      `SELECT "id" FROM clubs WHERE "slug" = $1`,
      dto.slug,
    );

    if (existing.length > 0) {
      throw new ConflictException(`Club with slug "${dto.slug}" already exists`);
    }

    // Execute all operations in a transaction
    const result = await this.prisma.$transaction(async (tx) => {
      // 1. Create the club
      const clubs = await tx.$queryRawUnsafe<Club[]>(
        `INSERT INTO clubs ("name", "slug", "address", "phone", "email", "isActive", "createdAt", "updatedAt")
         VALUES ($1, $2, $3, $4, $5, true, NOW(), NOW())
         RETURNING "id", "name", "slug", "address", "phone", "email",
                   "isActive", "createdAt", "updatedAt"`,
        dto.name,
        dto.slug,
        dto.address ?? null,
        dto.phone ?? null,
        dto.email ?? null,
      );

      const club = clubs[0];

      // 2. Create the license
      await tx.$executeRawUnsafe(
        `INSERT INTO licenses ("clubId", "type", "maxUsers", "maxClients", "isActive", "startDate", "endDate", "createdAt", "updatedAt")
         VALUES ($1, $2, $3, $4, true, NOW(), NOW() + INTERVAL '1 year', NOW(), NOW())`,
        club.id,
        licenseType,
        maxUsers,
        maxClients,
      );

      // 3. Create default club_modules based on license type
      const modules = DEFAULT_MODULES[licenseType];

      for (const moduleId of modules) {
        await tx.$executeRawUnsafe(
          `INSERT INTO club_modules ("clubId", "moduleId", "enabled", "createdAt", "updatedAt")
           VALUES ($1, $2, true, NOW(), NOW())
           ON CONFLICT ("clubId", "moduleId") DO NOTHING`,
          club.id,
          moduleId,
        );
      }

      // 4. Create default notification settings (one row per type)
      const notificationTypes = [
        'CLIENT_ASSIGNED',
        'SERVICE_BOOKED',
        'SERVICE_PAID',
        'PACKAGE_TOPUP',
        'BOOKING_CANCELLED',
        'TRAINING_DEDUCTED',
        'TRAININGS_ENDING',
        'CLIENT_RETURNED',
        'CLIENT_BIRTHDAY',
      ];

      for (const notificationType of notificationTypes) {
        await tx.$executeRawUnsafe(
          `INSERT INTO notification_settings ("id", "clubId", "notificationType", "enabled", "createdAt", "updatedAt")
           VALUES (gen_random_uuid(), $1, $2::"NotificationType", true, NOW(), NOW())
           ON CONFLICT ("clubId", "notificationType") DO NOTHING`,
          club.id,
          notificationType,
        );
      }

      return club;
    });

    this.logger.log(
      `Provisioned club "${result.name}" (${result.id}) with license ${licenseType}`,
    );

    return result;
  }

  /**
   * Deprovision (soft-delete) a club by marking it inactive.
   * Does not delete data — only prevents access.
   */
  async deprovisionClub(clubId: string): Promise<void> {
    await this.prisma.$executeRawUnsafe(
      `UPDATE clubs SET "isActive" = false, "updatedAt" = NOW() WHERE "id" = $1`,
      clubId,
    );

    // Also deactivate the license
    await this.prisma.$executeRawUnsafe(
      `UPDATE licenses SET "isActive" = false, "updatedAt" = NOW() WHERE "clubId" = $1`,
      clubId,
    );

    this.logger.log(`Deprovisioned club ${clubId}`);
  }
}
