import axios, { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from "axios";
import { AuthLogger } from "@/lib/authLogger";

const authLog = (event: string, detail: string) => {
  if (typeof window === "undefined") return;
  try {
    AuthLogger.log(event as any, detail);
  } catch {
    // 로거 실패해도 앱 동작에 영향 없음
  }
};

// API URL 동적 설정 - 환경변수 우선 사용
const getApiBaseUrl = (): string => {
  if (process.env.NEXT_PUBLIC_API_URL) {
    return process.env.NEXT_PUBLIC_API_URL;
  }

  if (typeof window !== "undefined") {
    const currentHost = window.location.hostname;
    const currentPort = window.location.port;

    if (currentHost === "v1.vexplor.com") {
      return "https://api.vexplor.com/api";
    }

    if (
      (currentHost === "localhost" || currentHost === "127.0.0.1") &&
      (currentPort === "9771" || currentPort === "3000")
    ) {
      return "http://localhost:8080/api";
    }
  }

  return "http://localhost:8080/api";
};

export const API_BASE_URL = getApiBaseUrl();

// 이미지 URL을 완전한 URL로 변환하는 함수
export const getFullImageUrl = (imagePath: string): string => {
  if (!imagePath) return "";

  if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
    return imagePath;
  }

  if (imagePath.startsWith("/uploads")) {
    if (typeof window !== "undefined") {
      const currentHost = window.location.hostname;

      if (currentHost === "v1.vexplor.com") {
        return `https://api.vexplor.com${imagePath}`;
      }

      if (currentHost === "localhost" || currentHost === "127.0.0.1") {
        return `http://localhost:8080${imagePath}`;
      }
    }

    const baseUrl = API_BASE_URL.replace(/\/api$/, "");
    if (baseUrl.startsWith("http://") || baseUrl.startsWith("https://")) {
      return `${baseUrl}${imagePath}`;
    }

    return imagePath;
  }

  return imagePath;
};

// ============================================
// JWT 토큰 관리 유틸리티
// ============================================
const TokenManager = {
  getToken: (): string | null => {
    if (typeof window !== "undefined") {
      return localStorage.getItem("authToken");
    }
    return null;
  },

  setToken: (token: string): void => {
    if (typeof window !== "undefined") {
      localStorage.setItem("authToken", token);
      document.cookie = `authToken=${token}; path=/; max-age=86400; SameSite=Lax`;
    }
  },

  removeToken: (): void => {
    if (typeof window !== "undefined") {
      localStorage.removeItem("authToken");
      document.cookie = "authToken=; path=/; max-age=0; SameSite=Lax";
    }
  },

  isTokenExpired: (token: string): boolean => {
    try {
      const payload = JSON.parse(atob(token.split(".")[1]));
      return payload.exp * 1000 < Date.now();
    } catch {
      return true;
    }
  },

  // 만료 30분 전부터 갱신 대상
  isTokenExpiringSoon: (token: string): boolean => {
    try {
      const payload = JSON.parse(atob(token.split(".")[1]));
      const expiryTime = payload.exp * 1000;
      const currentTime = Date.now();
      const thirtyMinutes = 30 * 60 * 1000;
      return expiryTime - currentTime < thirtyMinutes && expiryTime > currentTime;
    } catch {
      return false;
    }
  },

  getTimeUntilExpiry: (token: string): number => {
    try {
      const payload = JSON.parse(atob(token.split(".")[1]));
      return payload.exp * 1000 - Date.now();
    } catch {
      return 0;
    }
  },
};

// ============================================
// 토큰 갱신 로직 (중복 요청 방지)
// ============================================
let isRefreshing = false;
let refreshSubscribers: Array<(token: string) => void> = [];
let failedRefreshSubscribers: Array<(error: Error) => void> = [];

// 갱신 대기 중인 요청들에게 새 토큰 전달
const onTokenRefreshed = (newToken: string) => {
  refreshSubscribers.forEach((callback) => callback(newToken));
  refreshSubscribers = [];
  failedRefreshSubscribers = [];
};

// 갱신 실패 시 대기 중인 요청들에게 에러 전달
const onRefreshFailed = (error: Error) => {
  failedRefreshSubscribers.forEach((callback) => callback(error));
  refreshSubscribers = [];
  failedRefreshSubscribers = [];
};

// 갱신 완료 대기 Promise 등록
const waitForTokenRefresh = (): Promise<string> => {
  return new Promise((resolve, reject) => {
    refreshSubscribers.push(resolve);
    failedRefreshSubscribers.push(reject);
  });
};

const refreshToken = async (): Promise<string | null> => {
  try {
    const currentToken = TokenManager.getToken();
    if (!currentToken) {
      authLog("TOKEN_REFRESH_FAIL", "갱신 시도했으나 토큰 자체가 없음");
      return null;
    }

    authLog("TOKEN_REFRESH_START", `남은시간: ${Math.round(TokenManager.getTimeUntilExpiry(currentToken) / 60000)}분`);

    const response = await axios.post(
      `${API_BASE_URL}/auth/refresh`,
      {},
      {
        headers: {
          Authorization: `Bearer ${currentToken}`,
        },
      },
    );

    if (response.data?.success && response.data?.data?.token) {
      const newToken = response.data.data.token;
      TokenManager.setToken(newToken);
      authLog("TOKEN_REFRESH_SUCCESS", "토큰 갱신 완료");
      return newToken;
    }
    authLog("TOKEN_REFRESH_FAIL", `API 응답 실패: success=${response.data?.success}`);
    return null;
  } catch (err: any) {
    authLog("TOKEN_REFRESH_FAIL", `API 호출 에러: ${err?.response?.status || err?.message || "unknown"}`);
    return null;
  }
};

// ============================================
// 자동 토큰 갱신 (백그라운드)
// ============================================
let tokenRefreshTimer: ReturnType<typeof setInterval> | null = null;

const startAutoRefresh = (): void => {
  if (typeof window === "undefined") return;

  if (tokenRefreshTimer) {
    clearInterval(tokenRefreshTimer);
  }

  // 5분마다 토큰 상태 확인 (기존 10분 → 5분으로 단축)
  tokenRefreshTimer = setInterval(
    async () => {
      const token = TokenManager.getToken();
      if (token && TokenManager.isTokenExpiringSoon(token)) {
        await refreshToken();
      }
    },
    5 * 60 * 1000,
  );

  // 페이지 로드 시 즉시 확인
  const token = TokenManager.getToken();
  if (token && TokenManager.isTokenExpiringSoon(token)) {
    refreshToken();
  }
};

// 페이지 포커스 복귀 시 토큰 갱신 체크 (백그라운드 탭 throttle 대응)
const setupVisibilityRefresh = (): void => {
  if (typeof window === "undefined") return;

  document.addEventListener("visibilitychange", () => {
    if (!document.hidden) {
      const token = TokenManager.getToken();
      if (!token) {
        authLog("VISIBILITY_CHANGE", "탭 복귀 시 토큰 없음");
        return;
      }

      if (TokenManager.isTokenExpired(token)) {
        authLog("VISIBILITY_CHANGE", "탭 복귀 시 토큰 만료 감지 → 갱신 시도");
        refreshToken().then((newToken) => {
          if (!newToken) {
            authLog("REDIRECT_TO_LOGIN", "탭 복귀 후 토큰 갱신 실패로 리다이렉트");
            redirectToLogin();
          }
        });
      } else if (TokenManager.isTokenExpiringSoon(token)) {
        authLog("VISIBILITY_CHANGE", "탭 복귀 시 토큰 만료 임박 → 갱신 시도");
        refreshToken();
      }
    }
  });
};

// 사용자 활동 감지 기반 갱신
const setupActivityBasedRefresh = (): void => {
  if (typeof window === "undefined") return;

  let lastActivityCheck = Date.now();
  const activityThreshold = 5 * 60 * 1000; // 5분

  const handleActivity = (): void => {
    const now = Date.now();
    if (now - lastActivityCheck > activityThreshold) {
      const token = TokenManager.getToken();
      if (token && TokenManager.isTokenExpiringSoon(token)) {
        refreshToken();
      }
      lastActivityCheck = now;
    }
  };

  ["click", "keydown"].forEach((event) => {
    let throttleTimer: ReturnType<typeof setTimeout> | null = null;
    window.addEventListener(
      event,
      () => {
        if (!throttleTimer) {
          throttleTimer = setTimeout(() => {
            handleActivity();
            throttleTimer = null;
          }, 2000);
        }
      },
      { passive: true },
    );
  });
};

// 로그인 페이지 리다이렉트 (중복 방지)
let isRedirecting = false;
const redirectToLogin = (): void => {
  if (typeof window === "undefined") return;
  if (isRedirecting) return;
  if (window.location.pathname === "/login") return;

  authLog("REDIRECT_TO_LOGIN", `리다이렉트 실행 (from: ${window.location.pathname})`);
  isRedirecting = true;
  TokenManager.removeToken();
  window.location.href = "/login";
};

// 클라이언트 사이드에서 자동 갱신 시작
if (typeof window !== "undefined") {
  startAutoRefresh();
  setupVisibilityRefresh();
  setupActivityBasedRefresh();
}

// ============================================
// Axios 인스턴스 생성
// ============================================
export const apiClient = axios.create({
  baseURL: API_BASE_URL,
  timeout: 30000,
  headers: {
    "Content-Type": "application/json",
  },
  withCredentials: true,
});

// ============================================
// 요청 인터셉터
// ============================================
apiClient.interceptors.request.use(
  async (config: InternalAxiosRequestConfig) => {
    const token = TokenManager.getToken();

    if (token) {
      if (!TokenManager.isTokenExpired(token)) {
        config.headers.Authorization = `Bearer ${token}`;
      } else {
        authLog("TOKEN_EXPIRED_DETECTED", `요청 전 토큰 만료 감지 (${config.url}) → 갱신 시도`);
        const newToken = await refreshToken();
        if (newToken) {
          config.headers.Authorization = `Bearer ${newToken}`;
        } else {
          // 갱신 실패 시 인증 없는 요청을 보내면 TOKEN_MISSING 401 → 즉시 redirectToLogin 연쇄 장애
          // 요청 자체를 차단하여 호출부의 try/catch에서 처리하도록 함
          authLog("TOKEN_REFRESH_FAIL", `요청 인터셉터에서 갱신 실패 → 요청 차단 (${config.url})`);
          return Promise.reject(new Error("TOKEN_REFRESH_FAILED"));
        }
      }
    }

    // FormData 요청 시 Content-Type 자동 처리
    if (config.data instanceof FormData) {
      delete config.headers["Content-Type"];
    }

    // 언어 정보를 쿼리 파라미터에 추가 (GET 요청)
    if (config.method?.toUpperCase() === "GET") {
      let currentLang = "KR";

      if (typeof window !== "undefined") {
        if ((window as unknown as { __GLOBAL_USER_LANG?: string }).__GLOBAL_USER_LANG) {
          currentLang = (window as unknown as { __GLOBAL_USER_LANG: string }).__GLOBAL_USER_LANG;
        } else {
          const storedLocale = localStorage.getItem("userLocale");
          if (storedLocale) {
            currentLang = storedLocale;
          }
        }
      }

      if (config.params) {
        config.params.userLang = currentLang;
      } else {
        config.params = { userLang: currentLang };
      }
    }

    return config;
  },
  (error) => {
    return Promise.reject(error);
  },
);

// ============================================
// 응답 인터셉터
// ============================================
apiClient.interceptors.response.use(
  (response: AxiosResponse) => {
    // 백엔드에서 보내주는 새로운 토큰 처리
    const newToken = response.headers["x-new-token"];
    if (newToken) {
      TokenManager.setToken(newToken);
    }
    return response;
  },
  async (error: AxiosError) => {
    const status = error.response?.status;
    const url = error.config?.url;

    // 409 에러 (중복 데이터) - 조용하게 처리
    if (status === 409) {
      if (url?.includes("/check-duplicate") || url?.includes("/dataflow-diagrams")) {
        return Promise.reject(error);
      }
      return Promise.reject(error);
    }

    // 채번 규칙 미리보기 API 실패는 조용하게 처리
    if (url?.includes("/numbering-rules/") && url?.includes("/preview")) {
      return Promise.reject(error);
    }

    // 401 에러 처리 (핵심 개선)
    if (status === 401 && typeof window !== "undefined") {
      const errorData = error.response?.data as { error?: { code?: string; details?: string } };
      const errorCode = errorData?.error?.code;
      const errorDetails = errorData?.error?.details;
      const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };

      authLog("API_401_RECEIVED", `URL: ${url} | 코드: ${errorCode || "없음"} | 상세: ${errorDetails || "없음"}`);

      // 이미 재시도한 요청이면 로그인으로
      if (originalRequest?._retry) {
        authLog("REDIRECT_TO_LOGIN", `재시도 후에도 401 (${url}) → 로그인 리다이렉트`);
        redirectToLogin();
        return Promise.reject(error);
      }

      // 토큰 만료 에러 → 갱신 후 재시도
      if (errorCode === "TOKEN_EXPIRED" && originalRequest) {
        if (!isRefreshing) {
          isRefreshing = true;
          originalRequest._retry = true;

          try {
            authLog("API_401_RETRY", `토큰 만료로 갱신 후 재시도 (${url})`);
            const newToken = await refreshToken();
            if (newToken) {
              isRefreshing = false;
              onTokenRefreshed(newToken);
              originalRequest.headers.Authorization = `Bearer ${newToken}`;
              return apiClient.request(originalRequest);
            } else {
              isRefreshing = false;
              onRefreshFailed(new Error("토큰 갱신 실패"));
              authLog("REDIRECT_TO_LOGIN", `토큰 갱신 실패 (${url}) → 로그인 리다이렉트`);
              redirectToLogin();
              return Promise.reject(error);
            }
          } catch (refreshError) {
            isRefreshing = false;
            onRefreshFailed(refreshError as Error);
            authLog("REDIRECT_TO_LOGIN", `토큰 갱신 예외 (${url}) → 로그인 리다이렉트`);
            redirectToLogin();
            return Promise.reject(error);
          }
        } else {
          try {
            const newToken = await waitForTokenRefresh();
            originalRequest._retry = true;
            originalRequest.headers.Authorization = `Bearer ${newToken}`;
            return apiClient.request(originalRequest);
          } catch {
            return Promise.reject(error);
          }
        }
      }

      // TOKEN_MISSING, INVALID_TOKEN 등 → 로그인으로
      authLog("REDIRECT_TO_LOGIN", `복구 불가능한 인증 에러 (${errorCode || "UNKNOWN"}, ${url}) → 로그인 리다이렉트`);
      redirectToLogin();
    }

    return Promise.reject(error);
  },
);

// ============================================
// 공통 타입 및 헬퍼
// ============================================
export interface ApiResponse<T = unknown> {
  success: boolean;
  data?: T;
  message?: string;
  errorCode?: string;
}

export interface UserInfo {
  userId: string;
  userName: string;
  deptName?: string;
  companyCode?: string;
  userType?: string;
  userTypeName?: string;
  email?: string;
  photo?: string;
  locale?: string;
  isAdmin?: boolean;
}

export const getCurrentUser = async (): Promise<ApiResponse<UserInfo>> => {
  try {
    const response = await apiClient.get("/auth/me");
    return response.data;
  } catch (error: any) {
    return {
      success: false,
      message: error.response?.data?.message || error.message || "사용자 정보를 가져올 수 없습니다.",
      errorCode: error.response?.data?.errorCode,
    };
  }
};

export const apiCall = async <T>(
  method: "GET" | "POST" | "PUT" | "DELETE",
  url: string,
  data?: unknown,
): Promise<ApiResponse<T>> => {
  try {
    const response = await apiClient.request({
      method,
      url,
      data,
    });
    return response.data;
  } catch (error: unknown) {
    const axiosError = error as AxiosError;
    return {
      success: false,
      message:
        (axiosError.response?.data as { message?: string })?.message ||
        axiosError.message ||
        "알 수 없는 오류가 발생했습니다.",
      errorCode: (axiosError.response?.data as { errorCode?: string })?.errorCode,
    };
  }
};
