import { z } from 'zod';

/**
 * Environment validation schema
 */
export const EnvironmentSchema = z.enum(['development', 'staging', 'production', 'test']);

/**
 * Restart policy schema
 */
export const RestartPolicySchema = z.object({
  enabled: z.boolean().default(true),
  maxAttempts: z.number().int().min(0).default(3),
  delay: z.number().int().positive().default(5000),
  backoffMultiplier: z.number().positive().default(1.5),
  maxDelay: z.number().int().positive().default(30000),
});

/**
 * Service configuration schema
 */
export const ServiceConfigItemSchema = z.object({
  name: z.string().min(1),
  port: z.number().int().min(1000).max(65535),
  healthEndpoint: z.string(),
  startCommand: z.string().min(1),
  workingDirectory: z.string().optional(),
  dependencies: z.array(z.string()).optional(),
  restartPolicy: RestartPolicySchema.optional(),
  timeout: z.number().int().positive().default(30000),
  healthCheckInterval: z.number().int().positive().default(5000),
  gracefulShutdownTimeout: z.number().int().positive().default(5000),
  env: z.record(z.string()).optional(),
});

/**
 * Manager configuration schema
 */
export const ManagerConfigSchema = z.object({
  environment: EnvironmentSchema.default('development'),
  logLevel: z.enum(['error', 'warn', 'info', 'debug', 'trace']).default('info'),
  maxConcurrentServices: z.number().int().positive().default(10),
  globalTimeout: z.number().int().positive().default(60000),
  healthCheckInterval: z.number().int().positive().default(5000),
  enableMetrics: z.boolean().default(true),
  enableAutoRestart: z.boolean().default(true),
  services: z.array(ServiceConfigItemSchema).default([]),
});

/**
 * CLI configuration schema
 */
export const CLIConfigSchema = z.object({
  interactive: z.boolean().default(false),
  watch: z.boolean().default(false),
  verbose: z.boolean().default(false),
  colors: z.boolean().default(true),
  updateInterval: z.number().int().positive().default(1000),
  maxLogLines: z.number().int().positive().default(100),
  theme: z.enum(['default', 'minimal', 'compact']).default('default'),
});

// Export types
export type Environment = z.infer<typeof EnvironmentSchema>;
export type ServiceConfigItem = z.infer<typeof ServiceConfigItemSchema>;
export type ManagerConfig = z.infer<typeof ManagerConfigSchema>;
export type CLIConfig = z.infer<typeof CLIConfigSchema>;
export type RestartPolicy = z.infer<typeof RestartPolicySchema>;

/**
 * Default service configurations for ClipWhisperer
 */
export const DefaultServiceConfigs: ServiceConfigItem[] = [
  {
    name: 'Scraper',
    port: 9001,
    healthEndpoint: 'http://localhost:9001/health',
    startCommand: 'npm start',
    workingDirectory: 'Scraper',
    timeout: 30000,
    healthCheckInterval: 5000,
    gracefulShutdownTimeout: 5000,
  },
  {
    name: 'Narrator',
    port: 9002,
    healthEndpoint: 'http://localhost:9002/health',
    startCommand: 'npm start',
    workingDirectory: 'Narrator',
    dependencies: ['Scraper'],
    timeout: 30000,
    healthCheckInterval: 5000,
    gracefulShutdownTimeout: 5000,
  },
  {
    name: 'Renderer',
    port: 9004,
    healthEndpoint: 'http://localhost:9004/health',
    startCommand: 'npm start',
    workingDirectory: 'Renderer',
    dependencies: ['Narrator'],
    timeout: 30000,
    healthCheckInterval: 5000,
    gracefulShutdownTimeout: 5000,
  },
  {
    name: 'Hub',
    port: 9003,
    healthEndpoint: 'http://localhost:9003/health',
    startCommand: 'npm start',
    workingDirectory: 'Hub',
    dependencies: ['Scraper', 'Narrator', 'Renderer'],
    timeout: 30000,
    healthCheckInterval: 5000,
    gracefulShutdownTimeout: 5000,
  },
];

/**
 * Simple configuration manager for service orchestration
 */
export class ServiceConfig {
  private managerConfig: ManagerConfig;

  constructor(config?: Partial<ManagerConfig>) {
    // Load environment variables and apply defaults
    const environment = (process.env.NODE_ENV || 'development') as Environment;

    this.managerConfig = ManagerConfigSchema.parse({
      environment,
      logLevel: process.env.LOG_LEVEL || 'info',
      maxConcurrentServices: parseInt(process.env.MAX_CONCURRENT_SERVICES || '10'),
      globalTimeout: parseInt(process.env.GLOBAL_TIMEOUT || '60000'),
      healthCheckInterval: parseInt(process.env.HEALTH_CHECK_INTERVAL || '5000'),
      enableMetrics: process.env.ENABLE_METRICS !== 'false',
      enableAutoRestart: process.env.ENABLE_AUTO_RESTART !== 'false',
      services: DefaultServiceConfigs,
      ...config,
    });
  }

  public getEnvironment(): Environment {
    return this.managerConfig.environment;
  }

  public getServices(): ServiceConfigItem[] {
    return this.managerConfig.services;
  }

  public getService(serviceName: string): ServiceConfigItem | undefined {
    return this.managerConfig.services.find(service => service.name === serviceName);
  }

  public getManagerConfig(): ManagerConfig {
    return { ...this.managerConfig };
  }

  public isDevelopment(): boolean {
    return this.managerConfig.environment === 'development';
  }

  public isProduction(): boolean {
    return this.managerConfig.environment === 'production';
  }

  public isTest(): boolean {
    return this.managerConfig.environment === 'test';
  }

  public addService(service: ServiceConfigItem): void {
    const validatedService = ServiceConfigItemSchema.parse(service);
    this.managerConfig.services.push(validatedService);
  }

  public removeService(serviceName: string): boolean {
    const index = this.managerConfig.services.findIndex(service => service.name === serviceName);
    if (index !== -1) {
      this.managerConfig.services.splice(index, 1);
      return true;
    }
    return false;
  }

  public validateDependencies(): { valid: boolean; errors: string[] } {
    const errors: string[] = [];
    const serviceNames = new Set(this.managerConfig.services.map(s => s.name));

    // Check if all dependencies exist
    for (const service of this.managerConfig.services) {
      if (service.dependencies) {
        for (const dep of service.dependencies) {
          if (!serviceNames.has(dep)) {
            errors.push(`Service '${service.name}' depends on '${dep}', but '${dep}' is not configured`);
          }
        }
      }
    }

    // Check for circular dependencies
    const visited = new Set<string>();
    const visiting = new Set<string>();

    const hasCycle = (serviceName: string): boolean => {
      if (visited.has(serviceName)) return false;
      if (visiting.has(serviceName)) return true;

      visiting.add(serviceName);
      const service = this.getService(serviceName);
      if (service?.dependencies) {
        for (const dep of service.dependencies) {
          if (hasCycle(dep)) return true;
        }
      }
      visiting.delete(serviceName);
      visited.add(serviceName);
      return false;
    };

    for (const service of this.managerConfig.services) {
      if (hasCycle(service.name)) {
        errors.push(`Circular dependency detected involving service '${service.name}'`);
        break;
      }
    }

    return {
      valid: errors.length === 0,
      errors,
    };
  }
} 