/**
 * Copyright IBM Corp. 2024, 2025
 */
import { ErrorResponse, RequestHeader } from '../models/interface.js';
import { LogWrapper } from '../service/log-wrapper.js';
import { createObjectCsvStringifier } from 'csv-writer';
import { PDFDocument, StandardFonts } from 'pdf-lib';

export const errorsArray: ErrorResponse[] = [];

export const addErrorToResponse = (
  errorCode: string,
  field: string,
  description: string,
) => {
  errorsArray.push({
    code: errorCode,
    field: field,
    description: description,
  });
  LogWrapper.logDebug('0202', errorCode, field, description);
};

export const constructErrorResponse = () => {
  LogWrapper.logDebug('0003', 'Constructing error response.');

  const tempErrorsArray = [...errorsArray];
  errorsArray.length = 0;

  LogWrapper.logDebug('0203', `${tempErrorsArray.length}`);

  return {
    respCode: 400,
    message: 'Invalid Assets or Reference in the Zip',
    Endpoints: [],
    errors: tempErrorsArray,
  };
};

// TODO confirm and update sensitive keys and patterns
const SENSITIVE_KEYS = ['password', 'token'];
const SENSITIVE_PATTERNS = [
  /secret/i,
  /sensitive/i,
  /env/i,
  /auth/i,
  /credential/i,
  /api.?key/i,
  /bearer/i,
];

export const filterSensitiveData = (
  data: Record<string, any>,
): Record<string, any> => {
  const filter = (obj: Record<string, any>): Record<string, any> => {
    const result: Record<string, any> = {};

    for (const key in obj) {
      const lowerKey = key.toLowerCase();
      const isSensitive =
        SENSITIVE_KEYS.includes(lowerKey) ||
        SENSITIVE_PATTERNS.some((pattern) => pattern.test(lowerKey));

      if (isSensitive) {
        continue;
      }

      const value = obj[key];

      if (value && typeof value === 'object' && !Array.isArray(value)) {
        result[key] = filter(value); // Recurse into nested object
      } else {
        result[key] = value;
      }
    }

    return result;
  };

  return filter(data);
};

export const generateCSV = (data: any[]): Buffer => {
  if (data.length === 0) {
    return Buffer.from('', 'utf-8');
  }
  const headers = Object.keys(data[0] || {}).map((field) => ({
    id: field,
    title: field,
  }));
  const csvStringifier = createObjectCsvStringifier({ header: headers });
  const csv =
    csvStringifier.getHeaderString() + csvStringifier.stringifyRecords(data);
  return Buffer.from(csv, 'utf-8');
};

export const generatePDF = async (data: any[]): Promise<Buffer> => {
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage();

  const font = await pdfDoc.embedFont(StandardFonts.Helvetica);

  let y = page.getHeight() - 50;

  page.drawText('Run Report', {
    x: 200,
    y,
    size: 14,
    font,
  });

  y -= 30;

  data.forEach((entry, index) => {
    page.drawText(`Entry ${index + 1}`, {
      x: 50,
      y,
      size: 12,
      font,
    });
    y -= 20;

    for (const [key, value] of Object.entries(entry)) {
      page.drawText(`${key}: ${value}`, {
        x: 60,
        y,
        size: 10,
        font,
      });
      y -= 15;
    }

    y -= 10;
  });

  const pdfBytes = await pdfDoc.save();
  return Buffer.from(pdfBytes);
};

export const sanitizeAxiosResponse = (response: any, error: any = null) => {
  if (error) {
    // If we got an Axios error, extract meaningful info
    const cookieHeader = error.response?.headers.find(
      (h: RequestHeader) => h.key.toLowerCase() === 'set-cookie',
    );
    const cookies = cookieHeader?.value || []; // This is an array of cookie strings

    return {
      success: false,
      message: error.message,
      code: error.code || null,
      status: error.response?.status || null,
      statusText: error.response?.statusText || null,
      headers: error.response?.headers || {},
      cookies: parseSetCookies(cookies),
      config: {
        method: error.config?.method,
        url: error.config?.url,
        timeout: error.config?.timeout,
      },
      data: error.response?.data || null,
    };
  }

  const cookieHeader = response.headers.find(
    (h: RequestHeader) => h.key.toLowerCase() === 'set-cookie',
  );
  const cookies = cookieHeader?.value || []; // This is an array of cookie strings

  // Success case
  return {
    success: isSuccessStatus(response.status),
    status: response.status,
    statusText: response.statusText,
    headers: response.headers,
    cookies: parseSetCookies(cookies),
    config: {
      method: response.config?.method,
      url: response.config?.url,
      timeout: response.config?.timeout,
    },
    data: response.data,
    responseTime: response.responseTime,
  };
};

export const isSuccessStatus = (status: number): boolean => {
  return status >= 200 && status < 300;
};

export const parseSetCookies = (
  cookies: string[] | string,
): { key: string; value: string }[] => {
  // Handle empty, null, or undefined input
  if (!cookies || (typeof cookies === 'string' && cookies.trim() === '')) {
    return [];
  }

  // Convert single string to array if needed
  const cookieArray = Array.isArray(cookies) ? cookies : [cookies];

  return cookieArray.map((cookieStr) => {
    const parts = cookieStr.split(';').map((p) => p.trim());

    // First part is always the cookie itself: name=value
    const [nameValue] = parts;
    const [name, ...valParts] = nameValue.split('=');
    const value = valParts.join('=');

    return {
      key: name.trim(),
      value: value.trim(),
    };
  });
};
