import dayjs from 'dayjs';
import {API_BASE_URL} from '../env.json';
import {looseObject} from './types';
import {
  formDataRequests,
  isValidDate,
  parseQueryString,
  waitFor,
} from '../utils';
import {useToast} from '../contexts/ToastProvider';

// Global token expiry handler
let globalTokenExpiryHandler: (() => void) | null = null;

export const setTokenExpiryHandler = (handler: (() => void) | null) => {
  globalTokenExpiryHandler = handler;
};

const handleTokenExpiry = () => {
  if (globalTokenExpiryHandler) {
    globalTokenExpiryHandler();
  }
};

let isRefreshingRequestToken = false;

type RequestContent = 'form-data' | 'json';

type RequestObject = {
  requestUrl: string;
  customHeaders?: looseObject;
};

interface RequestPostObject extends RequestObject {
  requestData: looseObject;
  requestType?: RequestContent;
}

export const POST = async (params: RequestPostObject & {apiKey: string}) => {
  const {requestUrl, requestData, customHeaders, requestType, apiKey} = params;

  try {
    let data = formatRequestData(requestData, requestType);
    const requestHeaders = await getRequestHeaders(apiKey, requestType);
    const headers = {
      ...requestHeaders.headers,
      ...customHeaders,
    };

    const response = await fetch(`${API_BASE_URL}${requestUrl}`, {
      method: 'POST',
      headers,
      body: JSON.stringify(data),
    });

    const responseData = await response.json();

    if (!response.ok || responseData.error === true) {
      // Handle 401 unauthorized responses
      if (response.status === 401) {
        handleTokenExpiry();
      }

      return {
        error: extractDetailedErrorMessage(responseData),
        status: responseData?.status,
      };
    }

    return responseData;
  } catch (error: any) {
    const errorMessage = getErrorMessage(error);
    const errorResponse = error?.response || {};
    const errorData = errorResponse.data || {};
    return Promise.reject(errorData);
  }
};

export const PUT = async (params: RequestPostObject & {apiKey: string}) => {
  const {requestUrl, requestData, customHeaders, requestType, apiKey} = params;

  try {
    let data = formatRequestData(requestData, requestType);
    const requestHeaders = await getRequestHeaders(apiKey, requestType);
    const headers = {
      ...requestHeaders.headers,
      ...customHeaders,
    };

    const response = await fetch(`${API_BASE_URL}${requestUrl}`, {
      method: 'PUT',
      headers,
      body: JSON.stringify(data),
    });

    const responseData = await response.json();

    if (!response.ok || responseData.error === true) {
      // Handle 401 unauthorized responses
      if (response.status === 401) {
        handleTokenExpiry();
      }

      return {
        error: extractDetailedErrorMessage(responseData),
        status: responseData?.status,
      };
    }

    return responseData;
  } catch (error: any) {
    const errorMessage = getErrorMessage(error);
    const errorResponse = error?.response || {};
    const errorData = errorResponse.data || {};
    return Promise.reject(errorData);
  }
};

export const GET = async (params: RequestObject & {apiKey: string}) => {
  const {requestUrl, customHeaders, apiKey} = params;
  try {
    const requestHeaders = await getRequestHeaders(apiKey);
    const headers = {
      ...requestHeaders.headers,
      ...customHeaders,
    };

    let modifiedDateUrl = modifyUrl(requestUrl);
    const response = await fetch(`${API_BASE_URL}${modifiedDateUrl}`, {
      method: 'GET',
      headers,
    });

    // console.log(response, `response`);

    if (!response.ok) {
      // Handle 401 unauthorized responses
      if (response.status === 401) {
        handleTokenExpiry();
      }
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const responseData = await response.text();

    console.log(responseData, 'response-data');

    if (responseData.endsWith('.csv')) {
      return responseData;
    }

    return JSON.parse(responseData).responseData || JSON.parse(responseData);
  } catch (error: any) {
    // Handle fetch errors that might also be 401
    if (error?.message?.includes('401') || error?.response?.status === 401) {
      handleTokenExpiry();
    }
    const errorMessage = getErrorMessage(error);
    throw new Error(errorMessage);
  }
};

function extractDetailedErrorMessage(responseData: any): string {
  // Extract detailed validation errors if available
  if (responseData.errors) {
    // Handle validation errors object
    if (typeof responseData.errors === 'object') {
      const errorMessages = [];

      // Check for array of errors
      if (Array.isArray(responseData.errors)) {
        errorMessages.push(...responseData.errors);
      } else {
        // Check for object with error properties
        for (const [key, value] of Object.entries(responseData.errors)) {
          if (Array.isArray(value)) {
            errorMessages.push(...value);
          } else if (typeof value === 'string') {
            errorMessages.push(value);
          }
        }
      }

      if (errorMessages.length > 0) {
        return errorMessages.join('. ');
      }
    }
  }

  // Fallback to message or title
  return responseData.message ?? responseData.title ?? 'An error occurred. Please try again';
}

function getErrorMessage(error: any) {
  const errorCode = error?.response?.status;
  const errorData = error?.response?.data;
  const errorMessage = errorData?.message;

  if (error.message === 'Network Error') {
    return 'Check your network connection';
  }
  if (errorCode === 500) {
    return 'Ooops. Please try again';
  }

  if (error?.response?.message) {
    if (typeof error.response.message === 'string') {
      return error.response.message;
    }
  }

  return typeof errorData === 'string' && errorData
    ? errorData
    : errorMessage || error.message;
}

export async function getRequestHeaders(
  apiKey: string,
  requestType?: RequestContent,
) {
  const token = apiKey;
  return {
    headers: {
      ...(!!token && {authorization: `Bearer ${token}`}),
      'content-type':
        requestType === 'form-data'
          ? 'multipart/form-data'
          : 'application/json',
    },
  };
}

function modifyUrl(requestUrl: string) {
  if (requestUrl.includes('?') && requestUrl.split('?')[1].includes('Date')) {
    const currentParamsObject = parseQueryString(requestUrl.split('?')[1]);

    let newRequestParamsObject = {};
    for (const key in currentParamsObject) {
      if (
        key.includes('StartDate') ||
        key.includes('EndDate') ||
        (key === 'date' && currentParamsObject.StartDate === undefined)
      ) {
        const isSingleDateKey = key === 'date';
        if (key.includes('StartDate') || isSingleDateKey) {
          const keySuffix = isSingleDateKey ? '' : key.split('StartDate')[0];
          let endDateKey = `${keySuffix}EndDate`;

          if (endDateKey === 'createenddate') {
            endDateKey = 'createdenddate';
          }

          let startDateValue = currentParamsObject[key];
          let endDateValue = currentParamsObject[endDateKey];

          if (isValidDate(startDateValue)) {
            startDateValue = dayjs
              //@ts-ignore
              .utc(dayjs(startDateValue, 'YYYY-MM-DD').startOf('day'))
              .format();
          }

          if (isValidDate(endDateValue)) {
            endDateValue = dayjs
              //@ts-ignore
              .utc(dayjs(endDateValue, 'YYYY-MM-DD').endOf('day'))
              .format();
          }
          //@ts-ignore
          newRequestParamsObject[key] = startDateValue;
          //@ts-ignore
          newRequestParamsObject[endDateKey] = endDateValue;
        }
      } else {
        if (Array.isArray(currentParamsObject[key])) {
          //@ts-ignore
          newRequestParamsObject[key] = currentParamsObject[key][0];
        } else {
          //@ts-ignore
          newRequestParamsObject[key] = currentParamsObject[key];
        }
      }
    }

    const newParamsUrl = Object.keys(newRequestParamsObject)
      //@ts-ignore
      .map(key => key + '=' + newRequestParamsObject[key])
      .join('&');

    let modifiedDateUrl = `${requestUrl.split('?')[0]}?${newParamsUrl}`;
    return modifiedDateUrl;
  }
  return requestUrl;
}

export function formatRequestData(
  requestData: looseObject,
  requestType?: RequestContent,
) {
  if (requestType === 'form-data') {
    let fd = new FormData();
    for (const el in requestData) {
      let data = requestData[el];
      if (formDataRequests.includes(el)) {
        const uriParts = data?.split('.');
        const fileType = uriParts?.[uriParts.length - 1];
        if (data && uriParts && fileType) {
          // @ts-ignore
          fd.append(el, {
            uri: data,
            name: `${el}.${fileType}`,
            type: fileType.toLowerCase().includes('pdf')
              ? `application/${fileType.toLowerCase()}`
              : `image/${fileType.toLowerCase()}`,
          } as any);
        }
      } else {
        fd.append(el, data);
      }
    }
    return fd;
  }
  return requestData;
}
