import { SystemSettingsService } from './system-settings.service';
import { TenantSettingsService, EffectiveSetting } from './tenant-settings.service';
import { UserPreferencesService } from './user-preferences.service';
import { SystemSetting, TenantSetting, UserPreference } from '../entities';
import { logger } from '../../../shared/utils/logger.js';

/**
 * Combined settings response for a user context
 */
export interface UserSettingsContext {
  system: SystemSetting[];
  tenant: EffectiveSetting[];
  user: Record<string, any>;
}

/**
 * SettingsService
 *
 * Orchestrator service that combines system, tenant, and user preferences
 * to provide a unified settings interface.
 *
 * Features:
 * - Get all settings for a user context
 * - Resolve effective settings with proper inheritance
 * - Validate settings against rules
 */
export class SettingsService {
  constructor(
    private readonly systemSettingsService: SystemSettingsService,
    private readonly tenantSettingsService: TenantSettingsService,
    private readonly userPreferencesService: UserPreferencesService
  ) {}

  /**
   * Get all settings for a user context
   * Combines system, tenant effective, and user preferences
   */
  async getSettingsContext(
    userId: string,
    tenantId: string,
    planId?: string
  ): Promise<UserSettingsContext> {
    const [system, tenant, user] = await Promise.all([
      this.systemSettingsService.getPublicSettings(),
      this.tenantSettingsService.getAllEffective(tenantId, planId),
      this.userPreferencesService.getAllAsMap(userId),
    ]);

    logger.debug('Settings context loaded', {
      userId,
      tenantId,
      systemCount: system.length,
      tenantCount: tenant.length,
      userCount: Object.keys(user).length,
    });

    return { system, tenant, user };
  }

  /**
   * Get a specific setting value with full inheritance resolution
   */
  async getSetting(
    key: string,
    userId: string,
    tenantId: string,
    planId?: string
  ): Promise<any> {
    // User preferences have highest priority for user-specific keys
    if (key.startsWith('ui.') || key.startsWith('notifications.')) {
      const userValue = await this.userPreferencesService.getValue(userId, key);
      if (userValue !== null) {
        return userValue;
      }
    }

    // Tenant settings next
    const tenantSetting = await this.tenantSettingsService.getEffective(
      tenantId,
      key,
      planId
    );
    if (tenantSetting) {
      return tenantSetting.value;
    }

    // Fall back to system setting
    const systemValue = await this.systemSettingsService.getValue(key);
    return systemValue;
  }

  /**
   * Get multiple settings at once
   */
  async getSettings(
    keys: string[],
    userId: string,
    tenantId: string,
    planId?: string
  ): Promise<Record<string, any>> {
    const results: Record<string, any> = {};

    await Promise.all(
      keys.map(async (key) => {
        results[key] = await this.getSetting(key, userId, tenantId, planId);
      })
    );

    return results;
  }

  /**
   * Validate a setting value against its validation rules
   */
  async validateSetting(key: string, value: any): Promise<{ valid: boolean; errors: string[] }> {
    const setting = await this.systemSettingsService.getByKey(key);

    if (!setting) {
      return { valid: true, errors: [] };
    }

    const errors: string[] = [];
    const rules = setting.validationRules || {};

    // Type validation
    if (setting.dataType === 'number' && typeof value !== 'number') {
      errors.push(`Value must be a number`);
    }
    if (setting.dataType === 'boolean' && typeof value !== 'boolean') {
      errors.push(`Value must be a boolean`);
    }
    if (setting.dataType === 'string' && typeof value !== 'string') {
      errors.push(`Value must be a string`);
    }
    if (setting.dataType === 'array' && !Array.isArray(value)) {
      errors.push(`Value must be an array`);
    }

    // Range validation
    if (rules.min !== undefined && typeof value === 'number' && value < rules.min) {
      errors.push(`Value must be at least ${rules.min}`);
    }
    if (rules.max !== undefined && typeof value === 'number' && value > rules.max) {
      errors.push(`Value must be at most ${rules.max}`);
    }

    // Length validation
    if (rules.minLength !== undefined && typeof value === 'string' && value.length < rules.minLength) {
      errors.push(`Value must be at least ${rules.minLength} characters`);
    }
    if (rules.maxLength !== undefined && typeof value === 'string' && value.length > rules.maxLength) {
      errors.push(`Value must be at most ${rules.maxLength} characters`);
    }

    // Enum validation
    if (rules.enum && !rules.enum.includes(value)) {
      errors.push(`Value must be one of: ${rules.enum.join(', ')}`);
    }

    // Pattern validation
    if (rules.pattern && typeof value === 'string') {
      const regex = new RegExp(rules.pattern);
      if (!regex.test(value)) {
        errors.push(`Value does not match required pattern`);
      }
    }

    return {
      valid: errors.length === 0,
      errors,
    };
  }

  /**
   * Clear all caches
   */
  clearCache(): void {
    this.systemSettingsService.clearCache();
    logger.info('Settings cache cleared');
  }

  /**
   * Initialize settings for a new user
   */
  async initializeUser(userId: string): Promise<void> {
    await this.userPreferencesService.initializeDefaults(userId);
    logger.info('User settings initialized', { userId });
  }

  /**
   * Get settings by category across all levels
   */
  async getByCategory(
    category: string,
    userId: string,
    tenantId: string,
    planId?: string
  ): Promise<{
    system: SystemSetting[];
    tenant: EffectiveSetting[];
    user: Record<string, any>;
  }> {
    const [systemAll, tenantAll, userAll] = await Promise.all([
      this.systemSettingsService.getByCategory(category),
      this.tenantSettingsService.getAllEffective(tenantId, planId),
      this.userPreferencesService.getGroupedByCategory(userId),
    ]);

    return {
      system: systemAll,
      tenant: tenantAll.filter((s) => s.category === category),
      user: userAll[category] || {},
    };
  }
}
