import {
  Injectable,
  ConflictException,
  Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { TenantEntity } from '../../entities/tenant.entity';
import { UserEntity } from '../../entities/user.entity';
import { RoleEntity } from '../../entities/role.entity';
import {
  RolePermissionEntity,
  AccessLevel,
  ModuleName,
} from '../../entities/role-permission.entity';
import { TenantSettingsEntity } from '../../entities/tenant-settings.entity';
import { ServiceTypeEntity } from '../../entities/service-type.entity';
import { TaxTypeEntity } from '../../entities/tax-type.entity';
import { VehicleTypeEntity } from '../../entities/vehicle-type.entity';
import { CurrencyEntity, CurrencyFormat } from '../../entities/currency.entity';
import { OnboardTenantDto } from './dto/onboard-tenant.dto';

const SEED_SERVICE_TYPES = [
  { name: 'Hotels', code: 'hotels', description: 'Hotel accommodation services' },
  { name: 'Transport', code: 'transport', description: 'Transportation and transfer services' },
  { name: 'Activities', code: 'activities', description: 'Tours, excursions, and activities' },
];

const SEED_CURRENCIES = [
  { name: 'US Dollar', code: 'USD', symbol: '$', rounding: 1, format: CurrencyFormat.INTERNATIONAL },
  { name: 'Euro', code: 'EUR', symbol: '€', rounding: 1, format: CurrencyFormat.INTERNATIONAL },
  { name: 'British Pound', code: 'GBP', symbol: '£', rounding: 1, format: CurrencyFormat.INTERNATIONAL },
  { name: 'Indian Rupee', code: 'INR', symbol: '₹', rounding: 1, format: CurrencyFormat.INDIAN },
  { name: 'UAE Dirham', code: 'AED', symbol: 'د.إ', rounding: 1, format: CurrencyFormat.INTERNATIONAL },
];

const SEED_VEHICLE_TYPES = [
  { name: 'Bus' },
  { name: 'Coaster' },
  { name: 'Limousine' },
  { name: 'Mini Bus' },
  { name: 'Sedan' },
  { name: 'SUV' },
  { name: 'Van' },
];

const SEED_TAX_TYPES = [
  { name: 'GST', display_name: 'Goods and Services Tax (18%)', default_value: 18 },
  { name: 'VAT', display_name: 'Value Added Tax (20%)', default_value: 20 },
  { name: 'Service Tax', display_name: 'Service Tax (15%)', default_value: 15 },
  { name: 'Tourism Tax', display_name: 'Tourism Development Tax (5%)', default_value: 5 },
];

/**
 * Predefined roles seeded per scope doc (User Management → Role Management):
 * - Admin (full access to all modules)
 * - Operations Manager
 * - Trip Coordinator
 * - Sales Team
 * - Guest (read-only)
 *
 * Per CLAUDE.md:
 * - No Super Admin in Phase 1 — Admin is highest role, seeded during tenant onboarding
 * - Roles fully dynamic — these are starting templates, admin can modify
 */
const SEED_ROLES: Array<{
  name: string;
  description: string;
  defaultAccess: AccessLevel;
}> = [
  {
    name: 'Admin',
    description: 'Full access to all modules',
    defaultAccess: AccessLevel.DELETE,
  },
  {
    name: 'Operations Manager',
    description: 'Manage bookings, drivers, vehicles, and operations',
    defaultAccess: AccessLevel.EDIT,
  },
  {
    name: 'Trip Coordinator',
    description: 'Handle queries, quotations, and trip logistics',
    defaultAccess: AccessLevel.CREATE,
  },
  {
    name: 'Sales Team',
    description: 'Manage queries and quotations',
    defaultAccess: AccessLevel.VIEW,
  },
];

/**
 * TenantService — tenant onboarding with role + admin user seeding.
 *
 * ⚠️ NOTE: This service does NOT use TenantAwareRepository because
 * it creates the tenant itself and operates across tenant boundaries
 * during onboarding. All queries here explicitly include tenant_id.
 *
 * ⚠️ CLAUDE.md caution: Raw queries / cross-tenant ops → manually add tenant_id
 */
@Injectable()
export class TenantService {
  private readonly logger = new Logger(TenantService.name);

  constructor(
    @InjectRepository(TenantEntity)
    private readonly tenantRepo: Repository<TenantEntity>,
    @InjectRepository(UserEntity)
    private readonly userRepo: Repository<UserEntity>,
    @InjectRepository(RoleEntity)
    private readonly roleRepo: Repository<RoleEntity>,
    @InjectRepository(RolePermissionEntity)
    private readonly rolePermRepo: Repository<RolePermissionEntity>,
  ) {}

  /**
   * Onboard a new tenant:
   * 1. Create tenant record
   * 2. Seed predefined roles with default permissions
   * 3. Create Admin user assigned to Admin role
   *
   * This is a transactional operation.
   */
  async onboard(dto: OnboardTenantDto) {
    // Check subdomain uniqueness
    const existingTenant = await this.tenantRepo.findOne({
      where: { subdomain: dto.subdomain },
    });
    if (existingTenant) {
      throw new ConflictException('Subdomain already taken');
    }

    // Check admin email uniqueness globally
    const existingUser = await this.userRepo.findOne({
      where: { email: dto.admin_email },
    });
    if (existingUser) {
      throw new ConflictException('Admin email already registered');
    }

    // Run in transaction
    return this.tenantRepo.manager.transaction(async (manager) => {
      // 1. Create tenant
      const tenant = manager.create(TenantEntity, {
        name: dto.tenant_name,
        subdomain: dto.subdomain,
        is_active: true,
      });
      const savedTenant = await manager.save(tenant);
      const tenantId = savedTenant.id;

      this.logger.log(`Tenant created: ${savedTenant.name} (${tenantId})`);

      // 2. Seed roles + permissions
      let adminRoleId: string | null = null;
      const allModules = Object.values(ModuleName);

      for (const seedRole of SEED_ROLES) {
        const role = manager.create(RoleEntity, {
          name: seedRole.name,
          description: seedRole.description,
          is_system: true,
          tenant_id: tenantId,
          created_by: null,
          updated_by: null,
        });
        const savedRole = await manager.save(role);

        if (seedRole.name === 'Admin') {
          adminRoleId = savedRole.id;
        }

        // Create permissions for all 14 modules
        const permissions = allModules.map((mod) => {
          return manager.create(RolePermissionEntity, {
            role: savedRole,
            module: mod,
            access_level: seedRole.defaultAccess,
            tenant_id: tenantId,
            created_by: null,
            updated_by: null,
          });
        });

        await manager.save(permissions);

        this.logger.log(
          `Role seeded: ${seedRole.name} with ${seedRole.defaultAccess} access (${savedRole.id})`,
        );
      }

      // 3. Create Admin user
      const passwordHash = await bcrypt.hash(dto.admin_password, 10);

      const adminUser = manager.create(UserEntity, {
        name: dto.admin_name,
        email: dto.admin_email,
        password_hash: passwordHash,
        is_active: true,
        tenant_id: tenantId,
        role_id: adminRoleId!,
        created_by: null,
        updated_by: null,
      });
      const savedAdmin = await manager.save(adminUser);

      this.logger.log(
        `Admin user created: ${savedAdmin.email} (${savedAdmin.id})`,
      );

      // 4. Seed default currencies (before settings — settings references USD)
      let usdCurrencyId: string | null = null;
      for (const curr of SEED_CURRENCIES) {
        const currency = manager.create(CurrencyEntity, {
          ...curr,
          is_active: true,
          is_system: true,
          tenant_id: tenantId,
          created_by: null,
          updated_by: null,
        });
        const saved = await manager.save(currency);
        if (curr.code === 'USD') usdCurrencyId = saved.id;
      }

      this.logger.log(`Default currencies seeded for tenant ${tenantId}`);

      // 5. Seed default vehicle types
      for (const vt of SEED_VEHICLE_TYPES) {
        const vehicleType = manager.create(VehicleTypeEntity, {
          ...vt,
          is_active: true,
          is_system: true,
          tenant_id: tenantId,
          created_by: null,
          updated_by: null,
        });
        await manager.save(vehicleType);
      }

      this.logger.log(`Default vehicle types seeded for tenant ${tenantId}`);

      // 6. Seed default tenant settings (with USD as default currency)
      const settings = manager.create(TenantSettingsEntity, {
        tenant_id: tenantId,
        company_name: dto.tenant_name,
        default_currency_id: usdCurrencyId,
        primary_color: '#10b981',
        secondary_color: '#1e293b',
        distribution_mode: 'round_robin',
        created_by: null,
        updated_by: null,
      });
      await manager.save(settings);

      this.logger.log(`Default settings created for tenant ${tenantId}`);

      // 6. Seed default service types
      for (const st of SEED_SERVICE_TYPES) {
        const serviceType = manager.create(ServiceTypeEntity, {
          ...st,
          is_active: true,
          is_system: true,
          tenant_id: tenantId,
          created_by: null,
          updated_by: null,
        });
        await manager.save(serviceType);
      }

      this.logger.log(`Default service types seeded for tenant ${tenantId}`);

      // 7. Seed default tax types
      for (const tt of SEED_TAX_TYPES) {
        const taxType = manager.create(TaxTypeEntity, {
          ...tt,
          is_active: true,
          is_system: true,
          tenant_id: tenantId,
          created_by: null,
          updated_by: null,
        });
        await manager.save(taxType);
      }

      this.logger.log(`Default tax types seeded for tenant ${tenantId}`);

      return {
        tenant: {
          id: savedTenant.id,
          name: savedTenant.name,
          subdomain: savedTenant.subdomain,
        },
        admin: {
          id: savedAdmin.id,
          name: savedAdmin.name,
          email: savedAdmin.email,
        },
        roles_seeded: SEED_ROLES.map((r) => r.name),
        message: 'Tenant onboarded successfully',
      };
    });
  }

  /**
   * List all tenants (for internal/admin use only).
   */
  async findAll() {
    return this.tenantRepo.find({
      where: { is_active: true },
      order: { created_at: 'DESC' },
    });
  }
}
