import { api } from "@/services/api";
import type { ProfileTypeValue } from "@/services/companies.service";
import { companiesService } from "@/services/companies.service";
import type {
  LoginPayload,
  LoginResponse,
  OtpResponse,
  SignupPayload,
  SignupResponse,
} from "@/types/auth";
import type { Result } from "@/utils/result";
import { extractApiError } from "@/utils/result";
import { useQuery, useQueryClient } from "@tanstack/react-query";

function setCookie(name: string, value: string, days: number) {
  const expires = new Date();
  expires.setDate(expires.getDate() + days);
  document.cookie = `${name}=${value}; path=/; expires=${expires.toUTCString()}; SameSite=Lax`;
}

function getCookie(name: string): string | undefined {
  return document.cookie
    .split("; ")
    .find((row) => row.startsWith(`${name}=`))
    ?.split("=")[1];
}

const useAuth = () => {
  const queryClient = useQueryClient();
  const hasToken = !!getCookie("auth-token");

  const authQuery = useQuery(
    api.auth.getMyInfo.queryOptions({
      enabled: hasToken,
      retry: false,
      refetchOnMount: false,
      refetchOnReconnect: false,
      staleTime: 10 * 60 * 1000,
      refetchOnWindowFocus: false,
    }),
  );

  const companyQuery = useQuery(
    api.companies.getInfo.queryOptions({
      enabled: !!authQuery.data?.id,
      retry: false,

      staleTime(query) {
        // Fast-poll while anything is awaiting a backoffice decision: an
        // unapproved role, or a pending profile-edit review. This surfaces
        // approvals/rejections to the portal within a minute.
        if (
          query.state.data?.review?.status === "pending" ||
          query.state.data?.company?.companyProfiles?.find(
            (p) => p.status !== "active",
          )
        )
          return 60;

        return 10 * 60 * 1000;
      },
      refetchOnWindowFocus: false,
    }),
  );

  const isPending = authQuery.isPending && hasToken;
  const isAuthenticated = hasToken && !!authQuery.data && !authQuery.isError;

  const login = async (
    payload: LoginPayload,
  ): Promise<Result<LoginResponse>> => {
    try {
      const res = await api.auth.login.call(payload);
      setCookie("auth-token", res.token, 7);
      setCookie("refresh-token", res.refreshToken, 7);
      await authQuery.refetch();
      return { success: true, data: res };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const signup = async (
    payload: SignupPayload,
  ): Promise<Result<SignupResponse>> => {
    try {
      const res = await api.auth.createUser.call(payload);
      setCookie("auth-token", res.token, 7);
      setCookie("refresh-token", res.refreshToken, 7);
      await authQuery.refetch();
      return { success: true, data: res };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const setPassword = async (data: {
    newPassword: string;
    confirmPassword: string;
  }): Promise<Result<void>> => {
    try {
      const userId = authQuery.data?.id ?? "";
      const email = localStorage.getItem("otp-email") ?? "";
      const verificationCode = localStorage.getItem("otp") ?? "";
      await api.auth.setPassword.call({
        newPassword: data.newPassword,
        confirmPassword: data.confirmPassword,
        userId,
        email,
        verificationCode,
      });
      ["userId", "otp", "otp-phone", "otp-email"].forEach((k) =>
        localStorage.removeItem(k),
      );
      await queryClient.invalidateQueries({
        queryKey: api.auth.getMyInfo.queryKey(),
      });
      return { success: true, data: undefined };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const verifyOTP = async (otp: string): Promise<Result<OtpResponse>> => {
    try {
      const phone = localStorage.getItem("otp-phone") ?? "";
      const res = await api.auth.verifyOTP.call({ phone, otp });
      return { success: true, data: res };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const sendOTP = async (otp: string): Promise<Result<OtpResponse>> => {
    try {
      const phone = localStorage.getItem("otp-phone") ?? "";
      const res = await api.auth.sendOTP.call({ phone, otp });
      return { success: true, data: res };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const generateVerificationCode = async (
    type: string,
  ): Promise<Result<string>> => {
    try {
      const email = localStorage.getItem("otp-email") ?? "";
      const phoneNumber = localStorage.getItem("otp-phone") ?? "";
      const res = await api.auth.generateVerificationCode.call({
        email,
        phoneNumber,
        type,
      });
      return { success: true, data: res };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const companyInfo = isAuthenticated ? (companyQuery.data ?? null) : null;
  const companyType = companyInfo?.company?.type ?? null;
  const companyStatus = companyInfo?.company?.status ?? null;
  // A company can create bookings only once an admin has approved it (active).
  const isCompanyApproved = companyStatus === "active";
  const onboardingCompleted =
    companyInfo?.profile?.onboardingCompleted ?? false;
  const onboardingStep = companyInfo?.profile?.onboardingStep ?? null;

  // A booking/contract stamps its operational profile from the trade direction
  // at creation time, so there's no "active mode": the customer can create work
  // as long as they have at least one backoffice-approved operational role.
  const companyProfiles = companyInfo?.company?.companyProfiles ?? [];
  const hasActiveProfile = companyProfiles.some((p) => p.status === "active");
  const hasPendingProfile = companyProfiles.some((p) => p.status === "pending");
  const canBook = hasActiveProfile;

  // Profile-edit review: while a change request is pending the customer is
  // locked out of editing and of creating new contracts/bookings; a rejected
  // request surfaces the reviewer note so they can amend and resubmit.
  const review = companyInfo?.review ?? null;
  const reviewStatus = review?.status ?? null;
  const reviewNote = review?.note ?? null;
  const isUnderReview = reviewStatus === "pending";

  /** Refetch company info, dashboard, and bookings after a profile change. */
  const invalidateScopedData = async () => {
    await Promise.all([
      queryClient.invalidateQueries({
        queryKey: api.companies.getInfo.queryKey(),
      }),
      queryClient.invalidateQueries({
        queryKey: api.companies.getDashboard.queryKey(),
      }),
      queryClient.invalidateQueries({ queryKey: ["bookings"] }),
    ]);
  };

  /**
   * Add an operational role. The new role starts pending review, so the active
   * mode is left untouched — the user keeps working under their approved role.
   */
  const createProfile = async (
    type: ProfileTypeValue,
    licenseFiles: File[],
  ): Promise<Result<void>> => {
    try {
      const created = await api.companies.createCompanyProfile.call({ type });
      if (licenseFiles.length > 0) {
        await companiesService.uploadProfileLicense(created.id, licenseFiles);
      }
      await invalidateScopedData();
      return { success: true, data: undefined };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  /**
   * Resubmit a rejected operational role for approval — optionally replacing its
   * business license first (the common reason a role is rejected) — then refresh.
   */
  const reapplyProfile = async (
    profileId: string,
    licenseFiles: File[] = [],
  ): Promise<Result<void>> => {
    try {
      if (licenseFiles.length > 0) {
        await companiesService.uploadProfileLicense(profileId, licenseFiles);
      }
      await api.companies.reapplyProfile.call({ profileId });
      await invalidateScopedData();
      return { success: true, data: undefined };
    } catch (err) {
      return { success: false, error: extractApiError(err) };
    }
  };

  const logout = async () => {
    try {
      await api.auth.logout.call();
    } catch {
      // proceed with client-side cleanup even if server call fails
    }
    [
      "auth-token",
      "refresh-token",
      "auth-user",
      "current-position-id",
      "selected-position-id",
    ].forEach((name) => {
      document.cookie = `${name}=; Max-Age=0; path=/`;
    });
    localStorage.clear();
    queryClient.clear();
  };

  return {
    isPending,
    isAuthenticated,
    user: isAuthenticated ? (authQuery.data ?? null) : null,
    company: isAuthenticated ? (companyQuery.data ?? null) : null,
    customer: isAuthenticated ? (companyQuery.data ?? null) : null,
    canBook,
    hasActiveProfile,
    hasPendingProfile,
    companyType,
    companyStatus,
    isCompanyApproved,
    reviewStatus,
    reviewNote,
    isUnderReview,
    onboardingCompleted,
    onboardingStep,
    createProfile,
    reapplyProfile,
    login,
    signup,
    setPassword,
    verifyOTP,
    sendOTP,
    generateVerificationCode,
    logout,
    authQuery,
    companyQuery,
    customerQuery: companyQuery,
  };
};

export default useAuth;
