import { getCSRFToken } from '@/services/token.js';

class APIClient {
  constructor() {
    this.abortController = null;
    this.contextPath = document.querySelector('meta[name="context-path"]')?.getAttribute('content') || '/';
    this.contextPath = this.contextPath.endsWith('/') ? this.contextPath : this.contextPath + '/';
    this.CLIENT_URL = 'mgmt/api/test.do';
  }

  abort() {
    if (this.abortController) {
      this.abortController.abort();
      this.abortController = null;
    }
  }

  async sendAPIRequest(model, onLog = () => {}) {
    try {
      let variables = {};
      const processedModel = await this.executePreRequestScript(model, variables, onLog);
      const processedRequest = this.replacePlaceholdersWithVariables(processedModel, variables);

      onLog(`Request: ${processedRequest.method} ${processedRequest.path}`);

      const response = await this.makeRequest(processedRequest);
      const formattedResponse = this.formatResponse(response);
      const finalResponse = await this.executePostRequestScript(
          model.postRequestScript,
          variables,
          processedRequest,
          formattedResponse,
          onLog
      );

      return finalResponse;

    } catch (error) {
      if (error.name === 'AbortError') {
        throw new Error('Request was cancelled');
      }
      const errorMessage = error.message || 'Unknown error';
      onLog(`Error: ${errorMessage}`);
      throw new Error(`API Error: ${errorMessage}`);

    } finally {
      this.abortController = null;
    }
  }

  async makeRequest(request) {
    this.abortController = new AbortController();

    const response = await fetch(this.getClientUrl(), {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-XSRF-TOKEN': getCSRFToken()
      },
      body: JSON.stringify(request),
      signal: this.abortController.signal
    });

    if (!response.ok) {
      const data = await response.json();
      throw new Error(data.error || 'Unknown error');
    }

    return response.json();
  }

  formatResponse(data) {
    return {
      body: data.body,
      status: data.status,
      headers: data.headers ? Object.fromEntries(data.headers.map(h => [h.key, h.value])) : {},
      size: data.size
    };
  }

  getClientUrl() {
    return this.contextPath + this.CLIENT_URL;
  }

  replacePlaceholdersWithVariables(original, variables) {
    const request = JSON.parse(JSON.stringify(original));
    const mergedVariables = { ...window.globals, ...variables };
    console.log(mergedVariables);

    Object.entries(mergedVariables).forEach(([key, value]) => {
      const placeholder = new RegExp(`{{${key}}}`, 'g');
      console.log(placeholder);

      request.path = request.path?.replace(placeholder, value);
      request.requestBody = request.requestBody?.replace(placeholder, value);

      console.log("REQUEST BODY:",request.requestBody);

      if (request.headers) {
        const headersStr = JSON.stringify(request.headers).replace(placeholder, value);
        request.headers = JSON.parse(headersStr);
      }

      if (request.queryParams) {
        const paramsStr = JSON.stringify(request.queryParams).replace(placeholder, value);
        request.queryParams = JSON.parse(paramsStr);
      }
    });

    return request;
  }

  async executePreRequestScript(model, variables, onLog) {
    return new Promise((resolve, reject) => {
      // Create sandbox iframe if not exists
      let sandbox = document.getElementById('preRequest');
      if (!sandbox) {
        sandbox = document.createElement('iframe');
        sandbox.id = 'preRequest';
        sandbox.style.display = 'none';
        document.body.appendChild(sandbox);
      }

      // Setup communication channel
      window.preRequestComplete = (updatedModel, error) => {
        if (error) {
          reject(error);
        } else {
          resolve(updatedModel);
        }
      };

      // Setup context
      window.temp_variable = variables;
      window.currentRequest = model;
      window.consoleOutput = onLog;

      // Create sandbox content
      const sandboxContent = `
        <script>
          var oldLog = console.log;
          console.log = function(message) {
            if (window.parent.consoleOutput) {
              window.parent.consoleOutput(message);
            }
            oldLog.apply(console, arguments);
          };
          
          window.globals = window.parent.globals;
          window.variables = window.parent.temp_variable;
          window.request = window.parent.currentRequest;
      
          try {
            ${model.preRequestScript || ''}
            window.parent.preRequestComplete(window.request, null);
          } catch (error) { 
            window.parent.preRequestComplete(null, error);
          }
        </script>
      `;

      sandbox.srcdoc = sandboxContent;
    });
  }

  async executePostRequestScript(script, variables, request, response, onLog) {
    return new Promise((resolve, reject) => {
      // Create sandbox iframe if not exists
      let sandbox = document.getElementById('postRequest');
      if (!sandbox) {
        sandbox = document.createElement('iframe');
        sandbox.id = 'postRequest';
        sandbox.style.display = 'none';
        document.body.appendChild(sandbox);
      }

      // Setup communication channel
      window.postRequestComplete = (updatedResponse, error) => {
        if (error) {
          reject(error);
        } else {
          resolve(updatedResponse);
        }
      };

      // Setup context
      window.request = request;
      window.response = response;
      window.temp_variable = variables;
      window.consoleOutput = onLog;

      // Create sandbox content
      const sandboxContent = `
        <script>
          var oldLog = console.log;
          console.log = function(message) {
            if (window.parent.consoleOutput) {
              window.parent.consoleOutput(message);
            }
            oldLog.apply(console, arguments);
          };
          
          window.globals = window.parent.globals;
          window.variables = window.parent.temp_variable;
          window.request = window.parent.request;
          window.response = window.parent.response;
          
          try {
            ${script || ''}
            window.parent.postRequestComplete(window.response, null);
          } catch (error) {
            window.parent.postRequestComplete(window.response, error.toString());
          }
        </script>
      `;

      sandbox.srcdoc = sandboxContent;
    });
  }

}

export default APIClient;
