import axios, {
  AxiosError,
  AxiosInstance,
  InternalAxiosRequestConfig,
} from 'axios';
import {
  getAccessToken,
  handleTokenRefresh,
} from '../../login/utils/tokenRefresh';
import {
  OrderFilters,
  orderApiParamsSchema,
  orderResponseSchema,
  ordersResponseSchema,
  storesResponseSchema,
  customersResponseSchema,
  sellersResponseSchema,
  partnersResponseSchema,
  productsResponseSchema,
  unwrapApiData,
} from '../schemas/order.schema';
import {
  orderItemsResponseSchema,
  OrderItem,
  shipmentResponseSchema,
  Shipment,
  cargoMovementResponseSchema,
  CargoMovement,
  cuttingItemResponseSchema,
  CuttingItem,
} from '../schemas/order.item.schema';
import { Store } from '../schemas/store.schema';
import { Seller } from '../schemas/seller.schema';
import { Order } from '../types';

const ORDERS_API_URL = process.env.NEXT_PUBLIC_ORDERS_API_URL;

export const ordersApi: AxiosInstance = axios.create({
  baseURL: ORDERS_API_URL,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
  },
});

/**
 * Adiciona o token de autenticação aos cabeçalhos da requisição.
 * Executa apenas no ambiente do navegador (verifica a existência do objeto window).
 *
 * @param {InternalAxiosRequestConfig} config - A configuração da requisição Axios
 * @returns {InternalAxiosRequestConfig} A configuração modificada com o cabeçalho Authorization
 */
const addToken = (config: InternalAxiosRequestConfig) => {
  if (globalThis.window !== undefined) {
    const token = getAccessToken();
    if (token && config.headers) {
      config.headers.Authorization = `Bearer ${token}`;
    }
  }
  return config;
};

ordersApi.interceptors.request.use(addToken);

/**
 * Trata erros de resposta das requisições da API.
 * Tenta atualizar o token de autenticação se a requisição falhar.
 *
 * @param {AxiosError} error - O objeto de erro do Axios
 * @returns {Promise} Resultado da tentativa de atualização do token
 * @throws {AxiosError} Se não houver configuração de requisição original disponível
 */
const handleResponseError = async (error: AxiosError) => {
  const originalRequest = error.config as InternalAxiosRequestConfig & {
    _retry?: boolean;
  };

  if (!originalRequest) {
    throw error;
  }

  return handleTokenRefresh(error, originalRequest, ordersApi);
};

ordersApi.interceptors.response.use(
  (response) => response,
  handleResponseError
);

export const orderService = {
  /**
   * Busca pedidos com base nos filtros fornecidos.
   * Utiliza Zod para limpar e transformar os parâmetros automaticamente.
   *
   * @param {OrderFilters} filters - Critérios de filtro para buscar pedidos
   * @returns {Promise<Order[]>} Array de pedidos que correspondem aos filtros
   */
  findOrders: async (filters: OrderFilters): Promise<Order[]> => {
    const cleanParams = orderApiParamsSchema.parse(filters);
    const response = await ordersApi.get('/api/v1/orders/find', {
      params: cleanParams,
    });
    return unwrapApiData(response, ordersResponseSchema, []);
  },

  /**
   * Busca um pedido específico pelo seu ID.
   *
   * @param {number} id - O identificador único do pedido
   * @returns {Promise<Order | null>} O pedido com o ID especificado, ou null se não encontrado
   */
  findById: async (id: number): Promise<Order | null> => {
    const response = await ordersApi.get(`/orders/${id}`);
    return unwrapApiData(response, orderResponseSchema, null);
  },

  /**
   * Recupera todas as lojas disponíveis.
   *
   * @returns {Promise<Store[]>} Array de todas as lojas, ou array vazio se nenhuma for encontrada
   */
  findStores: async (): Promise<Store[]> => {
    const response = await ordersApi.get('/api/v1/data-consult/stores');
    return unwrapApiData(response, storesResponseSchema, []);
  },

  /**
   * Busca clientes por nome.
   * Retorna array vazio se o termo de busca tiver menos de 2 caracteres.
   *
   * @param {string} name - O nome do cliente a ser buscado (mínimo 2 caracteres)
   * @returns {Promise<Array<{id: number, name: string, estcob: string}>>} Array de clientes correspondentes com os campos id, name e estcob
   */
  findCustomers: async (
    name: string
  ): Promise<Array<{ id: number; name: string; estcob: string }>> => {
    if (!name || name.trim().length < 2) return [];

    const response = await ordersApi.get(
      `/api/v1/clientes/${encodeURIComponent(name)}`
    );
    return unwrapApiData(response, customersResponseSchema, []);
  },

  findSellers: async (): Promise<Seller[]> => {
    const response = await ordersApi.get('/api/v1/data-consult/sellers');
    return unwrapApiData(response, sellersResponseSchema, []);
  },

  findOrderItems: async (orderId: number): Promise<OrderItem[]> => {
    const response = await ordersApi.get(`/api/v1/orders/itens/${orderId}`);
    return unwrapApiData(response, orderItemsResponseSchema, []);
  },


  findDelivery: async (
    orderId: number,
    includeCompletedDeliveries: boolean = true
  ): Promise<Shipment[]> => {
    const response = await ordersApi.get(
      `/api/v1/orders/delivery/${orderId}`,
      {
        params: { includeCompletedDeliveries },
      }
    );
    return unwrapApiData(response, shipmentResponseSchema, []);
  },

  findCargoMovement: async (orderId: number): Promise<CargoMovement[]> => {
    const response = await ordersApi.get(
      `/api/v1/orders/transfer/${orderId}`
    );
    return unwrapApiData(response, cargoMovementResponseSchema, []);
  },

  findCuttingItems: async (orderId: number): Promise<CuttingItem[]> => {
    const response = await ordersApi.get(
      `/api/v1/orders/cut-itens/${orderId}`
    );
    return unwrapApiData(response, cuttingItemResponseSchema, []);
  },

  /**
   * Busca parceiros por nome/CPF.
   * Retorna array vazio se o termo de busca tiver menos de 2 caracteres.
   *
   * @param {string} filter - O termo de busca (mínimo 2 caracteres)
   * @returns {Promise<Array<{id: number, cpf: string, nome: string}>>} Array de parceiros correspondentes
   */
  findPartners: async (
    filter: string
  ): Promise<Array<{ id: number; cpf: string; nome: string }>> => {
    if (!filter || filter.trim().length < 2) return [];

    const response = await ordersApi.get(
      `/api/v1/parceiros/${encodeURIComponent(filter)}`
    );
    return unwrapApiData(response, partnersResponseSchema, []);
  },

  /**
   * Busca produtos por código ou descrição.
   * Retorna array vazio se o termo de busca tiver menos de 2 caracteres.
   *
   * @param {string} term - O termo de busca (mínimo 2 caracteres)
   * @returns {Promise<Array<{id: number, description: string}>>} Array de produtos correspondentes
   */
  findProducts: async (
    term: string
  ): Promise<Array<{ id: number; description: string }>> => {
    if (!term || term.trim().length < 2) return [];

    const response = await ordersApi.get(
      `/api/v1/data-consult/products/${encodeURIComponent(term)}`
    );
    return unwrapApiData(response, productsResponseSchema, []);
  },


  
};
