import { Env } from './Env';

/**
 * Configuration template with placeholder support
 */
export class ConfigTemplate {
  public static readonly Empty: ConfigTemplate = new ConfigTemplate();

  private _contentText: string;
  private _params: string[];

  constructor(contentText: string = '') {
    this._contentText = contentText;
    this._params = this.extractParams();
  }

  public get content(): string {
    return this._contentText;
  }

  /**
   * Backward compatibility getter
   */
  public get Params(): string[] {
    return this.params;
  }

  public get params(): string[] {
    return [...this._params];
  }

  /**
   * Extracts @placeholder@ patterns from template content
   */
  private extractParams(): string[] {
    const regex = /@(\w+)@/g;
    const paramsSet = new Set<string>();
    let match;

    while ((match = regex.exec(this._contentText)) !== null) {
      paramsSet.add(match[1]);
    }

    return Array.from(paramsSet);
  }
}

/**
 * Main configuration container
 */
export class Config {
  public static readonly ENV_NAME_PARAM = 'env_name';

  public envs: Env[] = [];
  public template: ConfigTemplate = ConfigTemplate.Empty;

  /**
   * Sets environments (backward compatibility)
   */
  public addEnvs(envs: Env[]): void {
    this.envs = envs;
  }

  /**
   * Sets environments
   */
  public setEnvs(envs: Env[]): void {
    this.envs = envs;
  }

  /**
   * Sets template content (backward compatibility)
   */
  public addTemplate(content: string): void {
    this.setTemplate(content);
  }

  /**
   * Sets template content
   */
  public setTemplate(content: string): void {
    this.template = new ConfigTemplate(content);
  }

  /**
   * Gets template as JSON string
   */
  public getTemplateAsJson(): string {
    try {
      return this.template.content;
    } catch {
      return '{}';
    }
  }

  /**
   * Updates template by adding placeholders for environment params
   */
  public updateTemplateFromEnv(env: Env): void {
    let templateObj: Record<string, any> = {};

    // Try to parse existing template
    try {
      if (this.template.content.trim()) {
        templateObj = JSON.parse(this.template.content);
      }
    } catch {
      // Start fresh if invalid JSON
    }

    // Add placeholders for params that don't exist yet
    for (const param of env.params) {
      if (param.name && param.name.trim()) {
        const placeholder = `@${param.name}@`;

        if (!this.template.content.includes(placeholder)) {
          templateObj[`!!! ${param.name}`] = placeholder;
        }
      }
    }

    this.template = new ConfigTemplate(JSON.stringify(templateObj, null, 4));
  }

  /**
   * Validates that all template placeholders have corresponding params (backward compatibility)
   */
  public validateParams(): string[] {
    return this.validatePlaceholders();
  }

  /**
   * Validates that all template placeholders have corresponding params
   */
  public validatePlaceholders(): string[] {
    const defaultEnv = this.envs.find(e => e.name === 'DEFAULT');
    const customEnvs = this.envs.filter(e => e.name !== 'DEFAULT');

    // Collect param names from DEFAULT
    const defaultParamNames = new Set(
      defaultEnv?.getParamNames() || []
    );

    // Collect param names from all custom envs
    const customParamNames = new Set(
      customEnvs.flatMap(e => e.getParamNames())
    );

    // Find missing placeholders
    const missingParams: string[] = [];
    for (const placeholder of this.template.params) {
      if (placeholder === Config.ENV_NAME_PARAM) continue;

      const inDefault = defaultParamNames.has(placeholder);
      const inCustom = customParamNames.has(placeholder);

      if (!inDefault && !inCustom) {
        missingParams.push(placeholder);
      }
    }

    return missingParams;
  }

  /**
   * Creates a deep copy of the config
   */
  public clone(): Config {
    const cloned = new Config();
    cloned.envs = [...this.envs];
    cloned.template = this.template;
    return cloned;
  }
}
