/** Copyright (c) MuteCode **/

import chalk from 'chalk';

import { SiloConfig } from './siloConfig';
import { SiloLog } from './siloLog';

export class Silo {
  private siloLogo: string = `                                
                              :+#@@@@#=.           .-*@@@@%+:                             
                           -*@@@@@@@@@@@%*-     :+%@@@@@@@@@@@#=.                         
                       :+#@@@@@@@@@@@@@@@@@@#=*@@@@@@@@@@@@@@@@@@%+:                      
                    -+%@@@@@@@@@@@#%@@@@@@@@@@@@@@@@@@@%#@@@@@@@@@@@@*-.                  
                .=#@@@@@@@@@@@@*-   .=@@@@@@@@@@@@@@@*.   :+%@@@@@@@@@@@%+:               
            .-*%@@@@@@@@@@@#=:    -+%@@@@@@@@@@@@@@@@@%*-    .-*%@@@@@@@@@@@#=.           
          +%@@@@@@@@@@@%+:     -#@@@@@@@@@@@@#%@@@@@@@@@@@%=     .=#@@@@@@@@@@@%+         
          @@@@@@@@@@#=.        *@@@@@@@@@%+:   .=#@@@@@@@@@@         -*@@@@@@@@@@.        
          @@@@@@@*-            +@@@@@@%=.          -*@@@@@@%            :+@@@@@@@.        
          @@@@@@@              +@@@@@@#+++++++++++++*@@@@@@%              %@@@@@@.        
          @@@@@@@.             +@@@@@@@@@@@@@@@@@@@@@@@@@@@@              %@@@@@@.        
          @@@@@@@.             +@@@@@@*:::::::::::::-@@@@@@@              %@@@@@@.        
          @@@@@@@.             +@@@@@@+             .@@@@@@@              %@@@@@@.        
          @@@@@@@.             +@@@@@@@@@@@@@@@@@@@@@@@@@@@%              %@@@@@@.        
          @@@@@@@.             +@@@@@@%#############%@@@@@@%              %@@@@@@.        
          @@@@@@@.             +@@@@@@=             .@@@@@@%              %@@@@@@.        
          @@@@@@@.             +@@@@@@#+++++++++++++*@@@@@@@              %@@@@@@.        
          @@@@@@@.             +@@@@@@@@@@@@@@@@@@@@@@@@@@@@              %@@@@@@.        
          @@@@@@@.             +@@@@@@+.............-@@@@@@@              %@@@@@@.        
          @@@@@@@@*-.          +@@@@@@@#=.       .=*@@@@@@@@           :+%@@@@@@@.        
          @@@@@@@@@@@#+.       +@@@@@@@@@@%*- :+%@@@@@@@@@@@       .=*@@@@@@@@@@@.        
          .=#@@@@@@@@@@@%*-     -*@@@@@@@@@@@@@@@@@@@@@@@#=.    :=#@@@@@@@@@@@#+:         
              -*@@@@@@@@@@@@#=.    :+%@@@@@@@@@@@@@@@@*-     -*@@@@@@@@@@@@#=.            
                 :+%@@@@@@@@@@@%+:  -+@@@@@@@@@@@@@@@#-  .=#@@@@@@@@@@@%+-                
                    .-*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#=.                   
                        :+%@@@@@@@@@@@@@@@@%+-=#@@@@@@@@@@@@@@@@@*-                       
                           .=#@@@@@@@@@@#=.      -*@@@@@@@@@@%+:                          
                               :+#@@#+:             .=*@@%*-.                             
                                                                                                                                
                  :+       -++++-   =++++.   +++++   .+.  .+       :++++-                 
                  -@.      +%  #+   %+       @:      :@.  :@.      =@. **                 
                  -@.      +%  #+   %=.**.   @****   :@.  :@.      =@  **                 
                  -@.      +%  #+   %=  @:      -@   :@.  :@.      =@  **                 
                  :****-   -*++*-   +*+-*.   *****   .*.  .**+*+   :**+*=
  `;

  /**
   * The apiKey of the Silo server where to send the logs
   */
  private apiKey: string = '';

  /**
   * The URL of the Silo server where to send the logs
   */
  private apiUrl: string = '';

  /**
   * Application name
   */
  private application: string = '';

  constructor(apiKey: string, apiUrl: string, application: string) {
    this.apiKey = apiKey;
    this.apiUrl = apiUrl;
    this.application = application;
  }

  public showSiloArt(): void {
    console.log(`
      ${chalk.green(this.siloLogo)}
    `);
  }

  /**
   * Logs the config details
   */
  public showConfig(): void {
    const siloConfig = SiloConfig.getInstance();
    const configData = siloConfig.getConfig();
    console.log(`
      ${chalk.green('======================= CONFIG START =======================')}
      ${chalk.bgRedBright('apiKey:')} ${configData.apiKey}
      ${chalk.bgRed('apiUrl:')} ${configData.apiUrl}
      ${chalk.bgBlue('application:')} ${configData.application}
      ${chalk.green('======================== CONFIG END ========================')}
    `);
  }

  /**
   * Logs a formatted string with the log details
   */
  public console(log: SiloLog): void {
    console.log(`
      ${chalk.green('======================= LOG START =======================')}
      ${chalk.bgRed('service:')} ${log.service}
      ${chalk.bgBlue('shortDescription:')} ${log.shortDescription}
      ${chalk.bgCyan('detailedDescription:')} ${log.detailedDescription}
      ${chalk.bgMagenta('level:')} ${log.level}
      ${chalk.bgRedBright('user:')} ${log.user}
      ${chalk.bgCyanBright('path:')} ${log.path}
      ${chalk.bgYellowBright('statusCode:')} ${log.statusCode}
      ${chalk.bgYellow('timestamp:')} ${log.timestamp.toISOString()}
      ${chalk.green('======================== LOG END ========================')}
    `);
  }

  /**
   * Sends the log details to the specified apiUrl
   */
  public async send(log: SiloLog): Promise<Response> {
    const logEntry = {
      apiUrl: this.apiUrl,
      application: this.application,
      service: log.service,
      shortDescription: log.shortDescription,
      detailedDescription: log.detailedDescription,
      level: log.level,
      user: log.user,
      path: log.path,
      statusCode: log.statusCode,
      timestamp: log.timestamp,
    };

    const headers: HeadersInit = {
      'Content-Type': 'application/json',
    };

    // In case of apiKey authentication use only your server's secret apiKey as header 'x-api-key'
    if (this.apiKey) {
      headers['x-api-key'] = this.apiKey;
    }

    try {
      const response: Response = await fetch(this.apiUrl, {
        method: 'POST',
        headers,
        body: JSON.stringify(logEntry),
      });

      return response;
    } catch (error: any) {
      return error;
    }
  }
}

/**
 * Creates a Silo instance with configuration
 */
export function create(config: { apiKey: string; apiUrl: string; application: string }): Silo {
  const siloConfig = SiloConfig.getInstance();
  siloConfig.setConfig(config.apiKey, config.apiUrl, config.application);

  const configData = siloConfig.getConfig();

  return new Silo(
    configData.apiKey,
    configData.apiUrl,
    configData.application
  );
}
