import axios from 'axios';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';

export class BaseApiService<T, CreatePayloadGeneric, UpdatePayloadGeneric> {
  basePath: string;
  header?: Record<string, string>;

  constructor(basePath: string, header?: Record<string, string>) {
    this.basePath = basePath;
    this.header = header;
  }

  async getAllFetcher(endpoint: string): Promise<BaseApiResponse<T[]>> {
    return await httpClientFetcher<BaseApiResponse<T[]>>(endpoint);
  }

  async getAll(query?: Record<string, unknown>) {
    try {
      const getAllPath = this.basePath;
      const getAllRes = await httpClient<BaseApiResponse<T[]>>(getAllPath, {
        query,
      });

      return getAllRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse<T[]>>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async getSingle(id: number) {
    try {
      const getSinglePath = `${this.basePath}/${id}`;
      const getSingleRes = await httpClient<BaseApiResponse<T>>(getSinglePath);

      return getSingleRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse<T>>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async create(payload: CreatePayloadGeneric) {
    const isFormData =
      typeof FormData !== 'undefined' && payload instanceof FormData;
    try {
      const headers = isFormData
        ? { ...(this.header ?? {}) }
        : { 'Content-Type': 'application/json', ...(this.header ?? {}) };

      const createRes = await httpClient<BaseApiResponse<T>>(this.basePath, {
        method: 'POST',
        body: payload,
        headers,
      });
      return createRes;
    } catch (error: unknown) {
      if (axios.isAxiosError<BaseApiResponse<T>>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async update(id: number, payload: UpdatePayloadGeneric) {
    const isFormData =
      typeof FormData !== 'undefined' && payload instanceof FormData;
    try {
      const updatePath = `${this.basePath}/${id}`;

      const headers = isFormData
        ? { ...(this.header ?? {}) }
        : { 'Content-Type': 'application/json', ...(this.header ?? {}) };

      const updateRes = await httpClient<BaseApiResponse<T>>(updatePath, {
        method: 'PATCH',
        body: payload,
        headers,
      });
      return updateRes;
    } catch (error: unknown) {
      if (axios.isAxiosError<BaseApiResponse<T>>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async delete(id: number) {
    try {
      const deletePath = `${this.basePath}/${id}`;
      const deleteRes = await httpClient<BaseApiResponse>(deletePath, {
        method: 'DELETE',
      });
      return deleteRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async customRequest<ResponseType = T, PayloadType = unknown>(
    endpoint: string,
    options?: {
      method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
      payload?: PayloadType;
      params?: Record<string, string | number | boolean | undefined>;
    }
  ): Promise<ResponseType | undefined> {
    try {
      const urlBase = endpoint.startsWith('http')
        ? endpoint
        : `${this.basePath.replace(/\/$/, '')}/${endpoint.replace(/^\//, '')}`;

      const url = options?.params
        ? `${urlBase}?${new URLSearchParams(
            Object.entries(options.params).reduce(
              (acc, [key, value]) => {
                if (value !== undefined) acc[key] = String(value);
                return acc;
              },
              {} as Record<string, string>
            )
          )}`
        : urlBase;

      const res = await httpClient<ResponseType>(url, {
        method: options?.method || 'GET',
        body: options?.payload,
      });

      return res;
    } catch (error: unknown) {
      if (axios.isAxiosError<ResponseType>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }
}
