import axios, { AxiosError, AxiosRequestConfig } from "axios";

const API_BASE_URL = process.env.REACT_APP_API_URL;

// CSRF token management
let csrfToken: string | null = null;

// Token refresh state
let isRefreshing = false;
let failedQueue: Array<{
  resolve: (value?: any) => void;
  reject: (reason?: any) => void;
  config: AxiosRequestConfig;
}> = [];

const processQueue = (
  error: AxiosError | null,
  token: string | null = null
) => {
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(prom.config);
    }
  });
  failedQueue = [];
};

const api = axios.create({
  baseURL: API_BASE_URL,
  headers: {
    "Content-Type": "application/json",
  },
  withCredentials: true, // Enable cookies
});

// Fetch CSRF token
export const fetchCSRFToken = async (): Promise<string> => {
  try {
    const response = await api.get("/auth/csrf-token");
    csrfToken = response.headers["x-csrf-token"] || "";
    return csrfToken || "";
  } catch (error) {
    console.error("Failed to fetch CSRF token:", error);
    return "";
  }
};

// Reset CSRF token (call on logout)
export const resetCSRFToken = () => {
  csrfToken = null;
};

api.interceptors.request.use(async (config) => {
  // Add CSRF token to headers for state-changing requests
  const method = config.method?.toUpperCase() || "";
  if (["POST", "PUT", "DELETE", "PATCH"].includes(method)) {
    // If we don't have a CSRF token yet, try to fetch it
    if (!csrfToken) {
      // Skip fetching only for the CSRF token endpoint itself to avoid circular dependency
      const isCsrfEndpoint = config.url?.includes("/auth/csrf-token");
      if (!isCsrfEndpoint) {
        await fetchCSRFToken();
      }
    }

    // Add the token if we have it
    if (csrfToken) {
      config.headers["X-CSRF-Token"] = csrfToken;
    }
  }
  return config;
});

api.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const originalRequest = error.config as AxiosRequestConfig & {
      _retry?: boolean;
      _csrfRetry?: boolean;
    };

    // Handle CSRF token errors
    if (error.response?.status === 403) {
      const errorData = error.response?.data as any;
      if (
        errorData?.code === "CSRF_TOKEN_MISMATCH" &&
        !originalRequest._csrfRetry
      ) {
        // Don't retry OAuth endpoints - the authorization code is single-use
        const isOAuthEndpoint = originalRequest.url?.includes("/auth/google");
        if (isOAuthEndpoint) {
          return Promise.reject(error);
        }

        originalRequest._csrfRetry = true;

        // Try to fetch a new CSRF token and retry
        try {
          await fetchCSRFToken();
          // Retry the original request with new token
          return api(originalRequest);
        } catch (csrfError) {
          console.error("Failed to refresh CSRF token:", csrfError);
        }
      }
    }

    // Handle token expiration and authentication errors
    if (error.response?.status === 401) {
      const errorData = error.response?.data as any;

      // Try to refresh for token errors
      // Note: We can't check refresh token from JS (httpOnly cookies)
      // The backend will determine if refresh is possible
      if (
        (errorData?.code === "TOKEN_EXPIRED" ||
          errorData?.code === "NO_TOKEN") &&
        !originalRequest._retry
      ) {
        if (isRefreshing) {
          // If already refreshing, queue the request
          return new Promise((resolve, reject) => {
            failedQueue.push({ resolve, reject, config: originalRequest });
          });
        }

        originalRequest._retry = true;
        isRefreshing = true;

        try {
          // Try to refresh the token
          await api.post("/auth/refresh");
          isRefreshing = false;
          processQueue(null);

          // Also refresh CSRF token after auth refresh
          await fetchCSRFToken();

          // Retry the original request
          return api(originalRequest);
        } catch (refreshError) {
          isRefreshing = false;
          processQueue(refreshError as AxiosError);

          // Refresh failed - let React Router handle redirects via PrivateRoute
          return Promise.reject(refreshError);
        }
      }
    }

    return Promise.reject(error);
  }
);

export const authAPI = {
  register: (data: any) => api.post("/auth/register", data),
  login: (data: any) => api.post("/auth/login", data),
  googleLogin: (code: string) => api.post("/auth/google", { code }),
  logout: () => api.post("/auth/logout"),
  refresh: () => api.post("/auth/refresh"),
  getCSRFToken: () => api.get("/auth/csrf-token"),
  getStatus: () => api.get("/auth/status"),
  verifyEmail: (code: string) => api.post("/auth/verify-email", { code }),
  resendVerification: () => api.post("/auth/resend-verification"),
  forgotPassword: (email: string) =>
    api.post("/auth/forgot-password", { email }),
  verifyResetToken: (token: string) =>
    api.post("/auth/verify-reset-token", { token }),
  resetPassword: (token: string, newPassword: string) =>
    api.post("/auth/reset-password", { token, newPassword }),
};

export const userAPI = {
  getProfile: () => api.get("/users/profile"),
  updateProfile: (data: any) => api.put("/users/profile", data),
  uploadProfileImage: (formData: FormData) =>
    api.post("/users/profile/image", formData, {
      headers: {
        "Content-Type": "multipart/form-data",
      },
    }),
  getPublicProfile: (id: string) => api.get(`/users/${id}`),
  getAvailability: () => api.get("/users/availability"),
  updateAvailability: (data: any) => api.put("/users/availability", data),
  // Admin endpoints
  adminBanUser: (id: string, reason: string) =>
    api.post(`/users/admin/${id}/ban`, { reason }),
  adminUnbanUser: (id: string) => api.post(`/users/admin/${id}/unban`),
};

export const addressAPI = {
  getAddresses: () => api.get("/users/addresses"),
  createAddress: (data: any) => api.post("/users/addresses", data),
  updateAddress: (id: string, data: any) =>
    api.put(`/users/addresses/${id}`, data),
  deleteAddress: (id: string) => api.delete(`/users/addresses/${id}`),
};

export const itemAPI = {
  getItems: (params?: any) => api.get("/items", { params }),
  getItem: (id: string) => api.get(`/items/${id}`),
  createItem: (data: any) => api.post("/items", data),
  updateItem: (id: string, data: any) => api.put(`/items/${id}`, data),
  deleteItem: (id: string) => api.delete(`/items/${id}`),
  getRecommendations: () => api.get("/items/recommendations"),
  getItemReviews: (id: string) => api.get(`/items/${id}/reviews`),
  // Admin endpoints
  adminSoftDeleteItem: (id: string, reason: string) =>
    api.delete(`/items/admin/${id}`, { data: { reason } }),
  adminRestoreItem: (id: string) => api.patch(`/items/admin/${id}/restore`),
};

export const rentalAPI = {
  createRental: (data: any) => api.post("/rentals", data),
  getRentals: () => api.get("/rentals/renting"),
  getListings: () => api.get("/rentals/owning"),
  getPendingRequestsCount: () => api.get("/rentals/pending-requests-count"),
  updateRentalStatus: (id: string, status: string) =>
    api.put(`/rentals/${id}/status`, { status }),
  reviewRenter: (id: string, data: any) =>
    api.post(`/rentals/${id}/review-renter`, data),
  reviewItem: (id: string, data: any) =>
    api.post(`/rentals/${id}/review-item`, data),
  getRefundPreview: (id: string) => api.get(`/rentals/${id}/refund-preview`),
  getLateFeePreview: (id: string, actualReturnDateTime: string) =>
    api.get(`/rentals/${id}/late-fee-preview`, {
      params: { actualReturnDateTime },
    }),
  cancelRental: (id: string, reason?: string) =>
    api.post(`/rentals/${id}/cancel`, { reason }),
  declineRental: (id: string, reason?: string) =>
    api.put(`/rentals/${id}/decline`, { reason }),
  // Return status marking
  markReturn: (
    id: string,
    data: { status: string; actualReturnDateTime?: string }
  ) => api.post(`/rentals/${id}/mark-return`, data),
  reportDamage: (id: string, data: any) =>
    api.post(`/rentals/${id}/report-damage`, data),
  getRentalCostPreview: (data: {
    itemId: string;
    startDateTime: string;
    endDateTime: string;
  }) => api.post("/rentals/cost-preview", data),
  updatePaymentMethod: (id: string, stripePaymentMethodId: string) =>
    api.put(`/rentals/${id}/payment-method`, { stripePaymentMethodId }),
  // 3DS authentication endpoints
  getPaymentClientSecret: (rentalId: string) =>
    api.get(`/rentals/${rentalId}/payment-client-secret`),
  completePayment: (rentalId: string) =>
    api.post(`/rentals/${rentalId}/complete-payment`),
};

export const messageAPI = {
  getMessages: () => api.get("/messages"),
  getSentMessages: () => api.get("/messages/sent"),
  getConversations: () => api.get("/messages/conversations"),
  getMessage: (id: string) => api.get(`/messages/${id}`),
  sendMessage: (data: {
    receiverId: string;
    content: string;
    imageFilename?: string;
  }) => api.post("/messages", data),
  markAsRead: (id: string) => api.put(`/messages/${id}/read`),
  getUnreadCount: () => api.get("/messages/unread/count"),
};

export const forumAPI = {
  getPosts: (params?: any) => api.get("/forum/posts", { params }),
  getPost: (id: string) => api.get(`/forum/posts/${id}`),
  createPost: (data: {
    title: string;
    content: string;
    category: string;
    tags?: string[];
    zipCode?: string;
    latitude?: number;
    longitude?: number;
    imageFilenames?: string[];
  }) => api.post("/forum/posts", data),
  updatePost: (id: string, data: any) => api.put(`/forum/posts/${id}`, data),
  deletePost: (id: string) => api.delete(`/forum/posts/${id}`),
  updatePostStatus: (id: string, status: string) =>
    api.patch(`/forum/posts/${id}/status`, { status }),
  acceptAnswer: (postId: string, commentId: string | null) =>
    api.patch(`/forum/posts/${postId}/accept-answer`, { commentId }),
  getMyPosts: () => api.get("/forum/my-posts"),
  getTags: (params?: any) => api.get("/forum/tags", { params }),
  createComment: (
    postId: string,
    data: {
      content: string;
      parentId?: string;
      imageFilenames?: string[];
    }
  ) => api.post(`/forum/posts/${postId}/comments`, data),
  updateComment: (commentId: string, data: any) =>
    api.put(`/forum/comments/${commentId}`, data),
  deleteComment: (commentId: string) =>
    api.delete(`/forum/comments/${commentId}`),
  // Admin endpoints
  adminDeletePost: (id: string, reason: string) =>
    api.delete(`/forum/admin/posts/${id}`, { data: { reason } }),
  adminRestorePost: (id: string) =>
    api.patch(`/forum/admin/posts/${id}/restore`),
  adminDeleteComment: (id: string, reason: string) =>
    api.delete(`/forum/admin/comments/${id}`, { data: { reason } }),
  adminRestoreComment: (id: string) =>
    api.patch(`/forum/admin/comments/${id}/restore`),
  adminClosePost: (id: string) => api.patch(`/forum/admin/posts/${id}/close`),
  adminReopenPost: (id: string) => api.patch(`/forum/admin/posts/${id}/reopen`),
};

export const stripeAPI = {
  getCheckoutSession: (sessionId: string) =>
    api.get(`/stripe/checkout-session/${sessionId}`),
  createConnectedAccount: () => api.post("/stripe/accounts"),
  createAccountLink: (data: { refreshUrl: string; returnUrl: string }) =>
    api.post("/stripe/account-links", data),
  createAccountSession: () => api.post("/stripe/account-sessions"),
  getAccountStatus: () => api.get("/stripe/account-status"),
  createSetupCheckoutSession: (data: { rentalData?: any }) =>
    api.post("/stripe/create-setup-checkout-session", data),
};

export const mapsAPI = {
  placesAutocomplete: (data: {
    input: string;
    types?: string[];
    componentRestrictions?: { country: string };
    sessionToken?: string;
  }) => api.post("/maps/places/autocomplete", data),
  placeDetails: (data: { placeId: string; sessionToken?: string }) =>
    api.post("/maps/places/details", data),
  geocode: (data: {
    address: string;
    componentRestrictions?: { country: string };
  }) => api.post("/maps/geocode", data),
  getHealth: () => api.get("/maps/health"),
};

export const conditionCheckAPI = {
  submitConditionCheck: (
    rentalId: string,
    data: { checkType: string; imageFilenames: string[]; notes?: string }
  ) => api.post(`/condition-checks/${rentalId}`, data),
  getBatchConditionChecks: (rentalIds: string[]) =>
    api.get(`/condition-checks/batch`, {
      params: { rentalIds: rentalIds.join(",") },
    }),
  getAvailableChecks: (rentalIds: string[]) =>
    api.get("/condition-checks", {
      params: { rentalIds: rentalIds.join(",") },
    }),
};

export const feedbackAPI = {
  submitFeedback: (data: { feedbackText: string; url?: string }) =>
    api.post("/feedback", data),
};

export default api;
