import { AdminUser } from '@core/admin/domain/entities/AdminUser';
import { Email } from '@core/admin/domain/value-objects/Email';
import type { AdminUserRepository } from '@core/admin/domain/repositories/AdminUserRepository';
import { User } from '@core/identity/domain/entities/User';
import type { AuthRepository } from '@core/identity/domain/repositories/AuthRepository';
import type { PasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { PasswordHash } from '@core/identity/domain/value-objects/PasswordHash';
import { UserId } from '@core/identity/domain/value-objects/UserId';
import type { Logger } from '@core/shared/domain/Logger';
import { stableUuidFromSeedKey } from './racing/SeedIdHelper';

interface DemoUserSpec {
  email: string;
  password: string;
  needsAdminUser: boolean;
  needsPrimaryDriverId: boolean;
  roles: string[];
  displayName: string;
}

/**
 * SeedDemoUsers - Creates predefined demo users for testing and development
 * 
 * This class creates a canonical set of demo users with fixed emails and passwords.
 * It is idempotent and supports force reseeding via environment variables.
 */
export class SeedDemoUsers {
  private readonly demoUserSpecs: DemoUserSpec[] = [
    {
      email: 'demo.driver@example.com',
      password: 'Demo1234!',
      needsAdminUser: false,
      needsPrimaryDriverId: true,
      roles: ['user'],
      displayName: 'John Driver',
    },
    {
      email: 'demo.sponsor@example.com',
      password: 'Demo1234!',
      needsAdminUser: false,
      needsPrimaryDriverId: true,
      roles: ['sponsor'],
      displayName: 'Jane Sponsor',
    },
    {
      email: 'demo.owner@example.com',
      password: 'Demo1234!',
      needsAdminUser: true,
      needsPrimaryDriverId: true,
      roles: ['owner'],
      displayName: 'Alice Owner',
    },
    {
      email: 'demo.steward@example.com',
      password: 'Demo1234!',
      needsAdminUser: true,
      needsPrimaryDriverId: true,
      roles: ['user'],
      displayName: 'Bob Steward',
    },
    {
      email: 'demo.admin@example.com',
      password: 'Demo1234!',
      needsAdminUser: true,
      needsPrimaryDriverId: true,
      roles: ['admin'],
      displayName: 'Charlie Admin',
    },
    {
      email: 'demo.systemowner@example.com',
      password: 'Demo1234!',
      needsAdminUser: true,
      needsPrimaryDriverId: true,
      roles: ['admin'],
      displayName: 'Diana SystemOwner',
    },
    {
      email: 'demo.superadmin@example.com',
      password: 'Demo1234!',
      needsAdminUser: true,
      needsPrimaryDriverId: true,
      roles: ['admin'],
      displayName: 'Edward SuperAdmin',
    },
  ];

  constructor(
    private readonly logger: Logger,
    private readonly authRepository: AuthRepository,
    private readonly passwordHashingService: PasswordHashingService,
    private readonly adminUserRepository: AdminUserRepository,
  ) {}

  private getApiPersistence(): 'postgres' | 'inmemory' {
    const configured = process.env.GRIDPILOT_API_PERSISTENCE?.toLowerCase();
    if (configured === 'postgres' || configured === 'inmemory') {
      return configured;
    }

    if (process.env.NODE_ENV === 'test') {
      return 'inmemory';
    }

    return process.env.DATABASE_URL ? 'postgres' : 'inmemory';
  }

  private generateDeterministicId(seedKey: string, persistence: 'postgres' | 'inmemory'): string {
    if (persistence === 'postgres') {
      return stableUuidFromSeedKey(seedKey);
    }
    return seedKey;
  }

  private generatePrimaryDriverId(email: string, persistence: 'postgres' | 'inmemory'): string {
    // Use predefined IDs for demo users to match SeedRacingData
    const demoDriverIds: Record<string, string> = {
      'demo.driver@example.com': 'driver-1',
      'demo.sponsor@example.com': 'driver-2',
      'demo.owner@example.com': 'driver-3',
      'demo.steward@example.com': 'driver-4',
      'demo.admin@example.com': 'driver-5',
      'demo.systemowner@example.com': 'driver-6',
      'demo.superadmin@example.com': 'driver-7',
    };

    const seedKey = demoDriverIds[email] || `primary-driver-${email}`;
    return this.generateDeterministicId(seedKey, persistence);
  }

  async execute(): Promise<void> {
    const persistence = this.getApiPersistence();
    
    // Check for force reseed via environment variable
    const forceReseedRaw = process.env.GRIDPILOT_API_FORCE_RESEED;
    const forceReseed = forceReseedRaw !== undefined && forceReseedRaw !== '0' && forceReseedRaw.toLowerCase() !== 'false';
    
    this.logger.info(
      `[Bootstrap] Demo users seed precheck: forceReseed=${forceReseed}, persistence=${persistence}`,
    );

    // Check if all demo users already exist
    let allUsersExist = true;
    for (const spec of this.demoUserSpecs) {
      const existingUser = await this.authRepository.findByEmail(EmailAddress.create(spec.email));
      if (!existingUser) {
        allUsersExist = false;
        break;
      }
      
      // Also check for admin users if needed
      if (spec.needsAdminUser) {
        const existingAdmin = await this.adminUserRepository.findByEmail(Email.create(spec.email));
        if (!existingAdmin) {
          allUsersExist = false;
          break;
        }
      }
    }

    if (allUsersExist && !forceReseed) {
      this.logger.info('[Bootstrap] Demo users already exist, skipping');
      return;
    }

    if (forceReseed) {
      this.logger.info('[Bootstrap] Force reseed enabled - updating existing demo users');
    } else {
      this.logger.info('[Bootstrap] Starting demo users seed');
    }

    // Create or update each demo user
    for (const spec of this.demoUserSpecs) {
      await this.createOrUpdateDemoUser(spec, persistence);
    }

    this.logger.info(
      `[Bootstrap] Demo users seed completed: ${this.demoUserSpecs.length} users processed`,
    );
  }

  private async createOrUpdateDemoUser(spec: DemoUserSpec, persistence: 'postgres' | 'inmemory'): Promise<void> {
    const userId = this.generateDeterministicId(`demo-user-${spec.email}`, persistence);
    
    // Check if user exists
    const existingUser = await this.authRepository.findByEmail(EmailAddress.create(spec.email));
    
    // Hash the password
    const passwordHash = await this.passwordHashingService.hash(spec.password);
    
    if (existingUser) {
      // Update existing user
      const rehydrateProps: {
        id: string;
        displayName: string;
        email?: string;
        passwordHash?: PasswordHash;
        primaryDriverId?: string;
      } = {
        id: existingUser.getId().value,
        displayName: spec.displayName,
        email: spec.email,
        passwordHash: PasswordHash.fromHash(passwordHash),
      };
      
      if (spec.needsPrimaryDriverId) {
        rehydrateProps.primaryDriverId = this.generatePrimaryDriverId(spec.email, persistence);
      }
      
      const updatedUser = User.rehydrate(rehydrateProps);
      
      await this.authRepository.save(updatedUser);
      this.logger.debug(`[Bootstrap] Updated demo user: ${spec.email}`);
    } else {
      // Create new user
      const createProps: {
        id: UserId;
        displayName: string;
        email?: string;
        passwordHash?: PasswordHash;
        primaryDriverId?: string;
      } = {
        id: UserId.fromString(userId),
        displayName: spec.displayName,
        email: spec.email,
        passwordHash: PasswordHash.fromHash(passwordHash),
      };
      
      if (spec.needsPrimaryDriverId) {
        createProps.primaryDriverId = this.generatePrimaryDriverId(spec.email, persistence);
      }
      
      const user = User.create(createProps);
      
      await this.authRepository.save(user);
      this.logger.debug(`[Bootstrap] Created demo user: ${spec.email}`);
    }

    // Handle admin user if needed
    if (spec.needsAdminUser) {
      const adminUserId = this.generateDeterministicId(`demo-admin-${spec.email}`, persistence);
      const existingAdmin = await this.adminUserRepository.findByEmail(Email.create(spec.email));
      
      if (existingAdmin) {
        // Admin user exists, no update needed for now
        this.logger.debug(`[Bootstrap] Admin user already exists: ${spec.email}`);
      } else {
        // Create admin user
        const adminCreateProps: {
          id: string;
          email: string;
          roles: string[];
          status: string;
          displayName: string;
          primaryDriverId?: string;
        } = {
          id: adminUserId,
          email: spec.email,
          roles: spec.roles,
          status: 'active',
          displayName: spec.displayName,
        };
        
        if (spec.needsPrimaryDriverId) {
          adminCreateProps.primaryDriverId = this.generatePrimaryDriverId(spec.email, persistence);
        }
        
        const adminUser = AdminUser.create(adminCreateProps);
        
        await this.adminUserRepository.create(adminUser);
        this.logger.debug(`[Bootstrap] Created admin user: ${spec.email} with roles: ${spec.roles.join(', ')}`);
      }
    }
  }
}