import axios, { AxiosInstance, AxiosResponse } from 'axios';
import {
  GetAssetsQueryParams,
  GetAssetDetailParams,
  GetAssetMapParams,
  GetAssetMapResponse,
  GetAvailableAssetsQueryParams,
  GetLinkedAssetFamiliesResponse,
} from '@/dto/assets.dto';
import { AssetOptionField } from '@/components/v2/DialogSelectAsset/DialogSelectAsset.vue.d';
import { buildBodyParams } from '@/utils';
import { QueryParams } from '@/components/v2/DataTable/DataTable.vue.d';

export interface ServiceOptions {
  headers?: Record<string, unknown>;
  params?: Record<string, unknown>;
  usePrefix?: boolean;
}

export const API = ({
  headers = {},
  params = {},
  usePrefix = true,
}: ServiceOptions = {}): AxiosInstance => {
  const user = JSON.parse(localStorage.getItem('user') ?? '{}');
  const BASE_URL = import.meta.env.VITE_APP_TAGSAMURAI_API;

  const instance = axios.create({
    baseURL: `${BASE_URL}/fam/assets/v2${usePrefix ? '/assets' : ''}`,
    headers: {
      'Content-type': 'application/json',
      'Authorization': `Bearer ${user.token}`,
      ...headers,
    },
    params,
  });

  return instance;
};

const postAssetList = (
  params?: GetAssetsQueryParams,
): Promise<AxiosResponse> => {
  const body = buildBodyParams(params);
  return API().post('/list', body);
};

const postAssetOption = (
  params?: GetAssetsQueryParams,
): Promise<AxiosResponse> => {
  const body = buildBodyParams(params);
  return API().post('/list/options', body);
};

const getAllAssets = (params: GetAssetsQueryParams): Promise<AxiosResponse> => {
  return API({ params }).get('/');
};

/**
 * @deprecated - use postAssetAvailableList instead
 */
const getAvailableAssets = (
  params: GetAvailableAssetsQueryParams,
): Promise<AxiosResponse> => {
  return API({ params }).get('/available');
};

const postAssetAvailableList = (
  params?: GetAssetsQueryParams | GetAvailableAssetsQueryParams,
): Promise<AxiosResponse> => {
  const body = buildBodyParams(params);
  return API().post('/available/list', body);
};

const scanAsset = (tag: string): Promise<AxiosResponse> => {
  return API({ params: { tag } }).get('');
};

/**
 * @deprecated - Replaced with postAssetsList
 */
const getAssetsById = (
  _id: string,
  params: GetAvailableAssetsQueryParams,
): Promise<AxiosResponse> => {
  return API({ params: { _id, ...params } }).get('/by-id');
};

const postAvailableListOptions = (
  params?: QueryParams,
): Promise<AxiosResponse> => {
  const body = buildBodyParams(params);
  return API().post('/available/list/options', body);
};

const getOptions = (
  endpoint?: 'unlinked' | 'by-id',
  params?: Partial<Record<AssetOptionField, boolean>> & {
    group?: string;
    excludeId?: string;
    mode?: string; // For unlinked
  },
): Promise<AxiosResponse> => {
  return API({ params }).get(endpoint ? `/${endpoint}/options` : '/options');
};

const getUnlinkedAssets = (
  params: GetAvailableAssetsQueryParams,
): Promise<AxiosResponse> => {
  return API({ params }).get('/unlinked');
};

const getAssetDetail = (
  id: string,
  params?: GetAssetDetailParams,
): Promise<AxiosResponse> => {
  return API({ params }).get(`/${id}`);
};

const getLinkedAssetFamily = (
  id?: string,
): Promise<AxiosResponse<GetLinkedAssetFamiliesResponse>> => {
  return API({ params: { id } }).get('/family');
};

/**
 * @deprecated - Replaced with postAssetsList
 */
const matchAssetWithTag = (
  id: string,
  tag?: string,
): Promise<AxiosResponse> => {
  const params = {
    _id: JSON.stringify([id]),
    tag,
  };
  return API({ params }).get('/by-id');
};

const getAssetMaps = (
  params?: GetAssetMapParams,
): Promise<AxiosResponse<GetAssetMapResponse>> => {
  let abortController: AbortController | null = null;

  // Cancel previous request
  if (abortController) {
    (abortController as AbortController).abort();
  }

  abortController = new AbortController();

  return API({
    usePrefix: false,
    params: params as Record<string, unknown>,
  }).get('/asset-maps', { signal: abortController.signal });
};

export default {
  getAllAssets,
  postAssetList,
  postAssetOption,
  getAvailableAssets,
  postAssetAvailableList,
  postAvailableListOptions,
  getAssetsById,
  getAssetDetail,
  matchAssetWithTag,
  getLinkedAssetFamily,
  getUnlinkedAssets,
  getOptions,
  scanAsset,
  getAssetMaps,
};
