import { Env } from "./Env";


export class ConfigTemplate {
  public static Empty: ConfigTemplate = new ConfigTemplate();

  constructor(text: string = "") {
    this._contentText = text;
    this.extractParams();
  }

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

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

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

  private extractParams() {
    let regex = /@(\w+)@/g;
    let matches;
    let paramsSet = new Set<string>();

    while ((matches = regex.exec(this._contentText)) !== null) {
      if (matches.length > 1) {
        paramsSet.add(matches[1]);
      }
    }

    this._params = Array.from(paramsSet);
  }

}


export class Config {
  public static get ENV_NAME_PARAM(): string { return "env_name" };

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

  addEnvs(envs: Env[]) {
    this.envs = envs;
  }

  addTemplate(text: string) {
    this.template = new ConfigTemplate(text);
  }

  getTemplateAsJson(): string {
    try {
      return this.template.content ;
    } catch (error) {
      console.error("Error converting template content to JSON:", error);
      return "{}";
    }
  }

  /**
   * Updates the template JSON by adding/updating params from the given environment.
   * Params are added as "!!! paramName": "@paramName@" placeholder pairs.
   * If a param's @placeholder@ already exists in template, it won't be added.
   * Existing template content is preserved.
   */
  updateTemplateFromEnv(env: Env) {
    let templateObj: Record<string, any> = {};

    // Try to parse existing template as JSON
    try {
      if (this.template.content.trim()) {
        templateObj = JSON.parse(this.template.content);
      }
    } catch (e) {
      // If parsing fails, start with empty object
      console.warn("Template is not valid JSON, starting fresh");
    }

    // Add/update params from the environment as placeholders
    for (const param of env.params) {
      if (param.name && param.name.trim() !== "") {
        const placeholderValue = `@${param.name}@`;
        
        // Check if this placeholder already exists anywhere in the template
        const placeholderAlreadyExists = this.template.content.includes(placeholderValue);
        
        if (!placeholderAlreadyExists) {
          const placeholderKey = `!!! ${param.name}`;
          templateObj[placeholderKey] = placeholderValue;
        }
      }
    }

    // Convert back to formatted JSON string
    const newTemplateContent = JSON.stringify(templateObj, null, 4);
    this.template = new ConfigTemplate(newTemplateContent);
  }

  validateParams(): string[] {
    const envKeys = this.envs.map(env => env.params.map(param => param.name)).flat();
    const missingParams = this.template.Params.filter(param => param != Config.ENV_NAME_PARAM && !envKeys.includes(param));

    if (missingParams.length > 0) {
      console.error("Template: missing parameters in environments:", missingParams);
    }

    return missingParams;
  }

  /**
   * Validates that all @placeholders@ in template have corresponding params.
   * Checks DEFAULT env first, then all custom envs.
   * Returns array of placeholder names that are not defined.
   */
  validatePlaceholders(): string[] {
    const defaultEnv = this.envs.find(e => e.name === "DEFAULT");
    const customEnvs = this.envs.filter(e => e.name !== "DEFAULT");
    
    // Collect all param names from DEFAULT
    const defaultParamNames = new Set(
      defaultEnv?.params.map(p => p.name).filter(n => n && n.trim() !== "") || []
    );
    
    // Collect all param names from all custom envs
    const customParamNames = new Set(
      customEnvs.flatMap(e => e.params.map(p => p.name).filter(n => n && n.trim() !== ""))
    );
    
    // Extract all @placeholders@ from template
    const placeholderRegex = /@(\w+)@/g;
    const placeholdersInTemplate = new Set<string>();
    let match;
    while ((match = placeholderRegex.exec(this.template.content)) !== null) {
      placeholdersInTemplate.add(match[1]);
    }
    
    // Find placeholders that don't have matching params
    const missingParams: string[] = [];
    for (const placeholder of placeholdersInTemplate) {
      if (placeholder === Config.ENV_NAME_PARAM) continue; // Skip built-in
      
      // Check if exists in DEFAULT or in ANY custom env
      const inDefault = defaultParamNames.has(placeholder);
      const inCustom = customParamNames.has(placeholder);
      
      // Valid if: in DEFAULT, or in at least one custom env
      if (!inDefault && !inCustom) {
        missingParams.push(placeholder);
      }
    }
    
    return missingParams;
  }
}

