// App Global functions
"use client";
import axios, { AxiosError, AxiosRequestConfig } from 'axios';

import { create } from "zustand"; //JWT
import { jwtDecode } from "jwt-decode";//JWT


const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_BASE_URL,
  timeout: 7000,
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true, //JWT for access token
});

export default api;


export const api2 = axios.create({
  baseURL: "http://localhost:17800",
  timeout: 7000,
  headers: {
    'Content-Type': 'text/html; charset=UTF-8',
  },
});




//JWT access token part
export const SIGN_IN_PAGE_URI = "/signin";
export const TOK_REFRESH_URI = "/api/sval2.do";  // security value 2(token refresh)

type JwtPayload = {
  sub: string;  // gid
  user_id: string;
  nick_name: string;
  biz_group_id: string;
  permission: number;
}

type AuthState = {
  accessToken: string | null;
  tokenPayload: JwtPayload | null;
  setAccessToken: (token: string) => void;
  clearAuth: () => void;
};

export const useAuthStore = create<AuthState>((set: any) => ({
  accessToken: null,
  tokenPayload: null,

  setAccessToken: (token: string) => {
    set({ accessToken: token, tokenPayload: jwtDecode<JwtPayload>(token) });
  },
  clearAuth: () => set({ accessToken: null, tokenPayload: null }),
}));

type UiLoadingState = {
  pendingCount: number;
  treePendingCount: number;
  tablePendingCount: number;
  isLoading: boolean;
  isTreeLoading: boolean;
  isTableLoading: boolean;
  startLoading: (scope?: "tree" | "table" | "global") => void;
  endLoading: (scope?: "tree" | "table" | "global") => void;
};

export const useUiLoadingStore = create<UiLoadingState>((set, get) => ({
  pendingCount: 0,
  treePendingCount: 0,
  tablePendingCount: 0,
  isLoading: false,
  isTreeLoading: false,
  isTableLoading: false,
  startLoading: (scope = "global") => {
    const pendingNext = get().pendingCount + 1;
    const treeNext = scope === "tree" ? get().treePendingCount + 1 : get().treePendingCount;
    const tableNext = scope === "table" ? get().tablePendingCount + 1 : get().tablePendingCount;
    set({
      pendingCount: pendingNext,
      treePendingCount: treeNext,
      tablePendingCount: tableNext,
      isLoading: pendingNext > 0,
      isTreeLoading: treeNext > 0,
      isTableLoading: tableNext > 0,
    });
  },
  endLoading: (scope = "global") => {
    const pendingNext = Math.max(0, get().pendingCount - 1);
    const treeNext = scope === "tree" ? Math.max(0, get().treePendingCount - 1) : get().treePendingCount;
    const tableNext = scope === "table" ? Math.max(0, get().tablePendingCount - 1) : get().tablePendingCount;
    set({
      pendingCount: pendingNext,
      treePendingCount: treeNext,
      tablePendingCount: tableNext,
      isLoading: pendingNext > 0,
      isTreeLoading: treeNext > 0,
      isTableLoading: tableNext > 0,
    });
  },
}));

const TREE_LOADING_ENDPOINTS = [
  "/api/get-biz-group2.do",
  "/api/get-biz-group3.do",
];

const TABLE_LOADING_ENDPOINTS = [
  "/api/get-account.do",
  "/api/get-device.do",
  "/api/get-goods.do",
  "/api/get-goods-templates.do",
  "/api/get-terminal.do",
  "/api/get-transaction.do",
  "/api/get-u-transaction.do",
  "/api/get-sales.do",
  "/api/get-notice.do",
  "/api/get-voc.do",
  "/api/get-resource-board.do",
];

const resolveLoadingScope = (url?: string): "tree" | "table" | "global" => {
  if (!url) return "global";
  if (TREE_LOADING_ENDPOINTS.some((x) => url.includes(x))) return "tree";
  if (TABLE_LOADING_ENDPOINTS.some((x) => url.includes(x))) return "table";
  return "global";
};

api.interceptors.request.use((config) => {
  const token = useAuthStore.getState().accessToken;
  if (token) {
    config.headers = config.headers ?? {};
    config.headers.Authorization = `Bearer ${token}`;
  }
  const loadingScope = resolveLoadingScope(config.url);
  (config as AxiosRequestConfig & { __loadingScope?: "tree" | "table" | "global" }).__loadingScope = loadingScope;
  useUiLoadingStore.getState().startLoading(loadingScope);
  return config;
});

// refresh token part
let g_isRefreshing = false;
let g_queue_refresh_tok_value: {
  resolve: (token: string) => void;
  reject: (err: any) => void;
}[] = [];

api.interceptors.response.use(
  (response) => {
    const loadingScope = (response.config as AxiosRequestConfig & { __loadingScope?: "tree" | "table" | "global" }).__loadingScope ?? resolveLoadingScope(response.config?.url);
    useUiLoadingStore.getState().endLoading(loadingScope);
    return response;
  },

  async (error: AxiosError) => {
    const loadingScope = (error.config as AxiosRequestConfig & { __loadingScope?: "tree" | "table" | "global" } | undefined)?.__loadingScope ?? resolveLoadingScope(error.config?.url);
    useUiLoadingStore.getState().endLoading(loadingScope);
    const original = error.config as AxiosRequestConfig & { _retry?: boolean };

    if (!original) {
      return Promise.reject(error);
    }

    if (original._retry) {
      return Promise.reject(error);
    }

    const status = error.response?.status;

    if (status !== 401) {
      return Promise.reject(error);
    }

    if (original.url === TOK_REFRESH_URI) {
      useAuthStore.getState().clearAuth();
      window.location.href = SIGN_IN_PAGE_URI;
      return Promise.reject(error);
    }

    if (g_isRefreshing) {
      return new Promise((resolve, reject) => {
        g_queue_refresh_tok_value.push({ resolve, reject });
      }).then((newToken) => {
        original.headers = original.headers ?? {};
        original.headers.Authorization = `Bearer ${newToken}`;
        return api(original);
      });
    }

    original._retry = true;
    g_isRefreshing = true;

    try {
      const resp = await api.post(TOK_REFRESH_URI);
      const newToken = resp.data.result.sval1;

      useAuthStore.getState().setAccessToken(newToken);

      g_queue_refresh_tok_value.forEach(({ resolve }) => resolve(newToken));
      g_queue_refresh_tok_value = [];

      original.headers = original.headers ?? {};
      original.headers.Authorization = `Bearer ${newToken}`;
      return api(original);

    } catch (refreshError) {
      g_queue_refresh_tok_value.forEach(({ reject }) => reject(refreshError));
      g_queue_refresh_tok_value = [];

      useAuthStore.getState().clearAuth();
      window.location.href = SIGN_IN_PAGE_URI;
      return Promise.reject(refreshError);

    } finally {
      g_isRefreshing = false;
    }
  }
);

export const clearAuthStore = async () => {
  useAuthStore.getState().clearAuth();
};

export const refreshAuthStore = async () => {
  if (useAuthStore.getState().tokenPayload != null)
    return;

  try {
    const resp = await api.post(TOK_REFRESH_URI);
    const newToken = resp.data.result.sval1;

    useAuthStore.getState().setAccessToken(newToken);
  }
  catch (e) {}
};
//////////////////////////////////////////////////////////////////////////////


export const postFileDownload = (url: string, params: any) => {
  api.post(url, params, {responseType: 'blob'}).then((resp) => {
    const disposition = resp.headers['content-disposition'];
    let filename : string = ""; 
    if (disposition && disposition.indexOf('attachment') !== -1) { 
        const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; 
        const matches = filenameRegex.exec(disposition); 
        if (matches != null && matches[1])
            filename = matches[1].replace(/['"]/g, ''); 
    } 
    const blob = new Blob([resp.data], {type: resp.headers['content-type']});
    const link = document.createElement('a'); 
    link.href = window.URL.createObjectURL(blob); 
    //link.download = filename;
    link.download = decodeURIComponent(filename);
    //link.download = unespace(filename);
    link.click(); 

  }).catch((err) => {
    console.log(err);
    alert(err.message);
  });
}

// DB TIMESTAMP DATETIME modify (yyyy-MM-dd HH:mm:ss.0 -> yyyy-MM-dd HH:mm)
export const convertDateTime = (tsDateTime: string) => {
    if (tsDateTime == undefined)
        return "";

    return tsDateTime.replace(".0", "");
}

// DB TIMESTAMP DATETIME modify (UTC time -> yyyy-MM-dd HH:mm:ss)
export const convertDateTime2 = (tsDateTime: string) => {
    if (tsDateTime == undefined)
        return "";

    const localDate = new Date(tsDateTime);
    const formatted = localDate.toLocaleString("ko-KR", {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
      hour12: false, // 0~23hour
    });

    return formatted;
}

// DB TIMESTAMP DATETIME modify (UTC time -> yyyy-MM-dd HH:mm)
export const convertDateTime3 = (tsDateTime: string) => {
    if (tsDateTime == undefined || tsDateTime == "")
        return "";

    const localDate = new Date(tsDateTime);
    const formatted = localDate.toLocaleString("ko-KR", {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      //second: "2-digit",
      hour12: false, // 0~23hour
    });

    return formatted;
}

// yyyyMMddHHmmss => yyyy-MM-dd HH:mm:ss
export const convertDateTime4 = (tsDateTime: string) => {
  if (tsDateTime == undefined || tsDateTime == "")
    return "";

  const yyyy = tsDateTime.slice(0, 4);
  const mm = tsDateTime.slice(4, 6);
  const dd = tsDateTime.slice(6, 8);
  const HH = tsDateTime.slice(8, 10);
  const MM = tsDateTime.slice(10, 12);
  const SS = tsDateTime.slice(12, 14);

  return `${yyyy}-${mm}-${dd} ${HH}:${MM}:${SS}`;
}

// yyyyMMddHHmmss => yyyy-MM-dd HH:mm
export const convertDateTime5 = (tsDateTime: string) => {
  if (tsDateTime == undefined || tsDateTime == "")
    return "";

  const yyyy = tsDateTime.slice(0, 4);
  const mm = tsDateTime.slice(4, 6);
  const dd = tsDateTime.slice(6, 8);
  const HH = tsDateTime.slice(8, 10);
  const MM = tsDateTime.slice(10, 12);

  return `${yyyy}-${mm}-${dd} ${HH}:${MM}`;
}

// yyyyMMddHHmmss => yyyy-MM-dd HH:mm
export const convertDateTime6 = (tsDateTime: string) => {
  if (tsDateTime == undefined || tsDateTime == "")
    return "";

  let startPos = 0;
  let yy = tsDateTime.slice(startPos, startPos + 2);
  if (yy == "20") {
    startPos += 2;
    yy = tsDateTime.slice(startPos, startPos + 2);
  }

  startPos += 2;
  const mm = tsDateTime.slice(startPos, startPos + 2);

  startPos += 2;
  const dd = tsDateTime.slice(startPos, startPos + 2);

  startPos += 2;
  const HH = tsDateTime.slice(startPos, startPos + 2);

  startPos += 2;
  const MM = tsDateTime.slice(startPos, startPos + 2);

  return `20${yy}-${mm}-${dd} ${HH}:${MM}`;
}

export const getUrlParam = (key: string) => {
  if (window == undefined)
    return "";

  const params = new URLSearchParams(window.location.search);
  const value = params.get(key);
  if (value == null)
    return "";

  return value;
}

// 사업자등록번호 하이픈(-) 자동삽입
//const formatted = formatBusinessNumber("1234567890"); // "123-45-67890"
export function formatBusinessNumber(input: string): string {
  const allowed = input.replace(/[^0-9-]/g, "");
  const digits = allowed.replace(/-/g, "").substring(0, 10);

  let formatted = digits;
  if (digits.length > 3 && digits.length <= 5) {
    formatted = digits.replace(/^(\d{3})(\d{1,2})$/, "$1-$2");
  } else if (digits.length > 5) {
    formatted = digits.replace(/^(\d{3})(\d{2})(\d{1,5})$/, "$1-$2-$3");
  }

  if (allowed.endsWith("-") && !formatted.endsWith("-")) {
    formatted += "-";
  }

  return formatted;
}

// 사업자등록번호 유효성 검사
//console.log(isValidBusinessNumber("123-45-67890")); // false
//console.log(isValidBusinessNumber("220-81-62517")); // true (실제 유효 번호 예시)
export function isValidBusinessNumber(input: string): boolean {

  return true;


  const digits = input.replace(/\D/g, "");
  if (digits.length !== 10)
    return false;

  const weights = [1, 3, 7, 1, 3, 7, 1, 3, 5];
  let sum = 0;

  for (let i = 0; i < 9; i++) {
    const num = parseInt(digits.charAt(i), 10);
    sum += num * weights[i];
  }

  sum += Math.floor((parseInt(digits.charAt(8), 10) * 5) / 10);

  const checkDigit = (10 - (sum % 10)) % 10;
  return checkDigit === parseInt(digits.charAt(9), 10);
}

