"use client";

import type {
  AuthSession,
  EquipmentCategory,
  EquipmentItem,
  RentalBooking,
  TimelineResponse,
  UserPreferences,
  UserRole
} from "@rental/shared";

import { safeStorageGet, safeStorageRemove, safeStorageSet } from "./client-storage";

export type UserRecord = {
  id: string;
  name: string;
  email: string;
  role: UserRole;
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
};

export const sessionStorageKey = "rental.session";
export const sessionSyncEventName = "rental.session.sync";
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? "/api";

const dispatchSessionSync = () => {
  if (typeof window === "undefined") {
    return;
  }

  window.dispatchEvent(new Event(sessionSyncEventName));
};

const formatApiError = (payload: unknown) => {
  const fallback = "Request failed";

  if (!payload || typeof payload !== "object") {
    return fallback;
  }

  const errorPayload = payload as {
    error?: unknown;
    details?: unknown;
  };
  const baseMessage = typeof errorPayload.error === "string" ? errorPayload.error : fallback;

  if (!errorPayload.details) {
    return baseMessage;
  }

  if (typeof errorPayload.details === "string") {
    return `${baseMessage}: ${errorPayload.details}`;
  }

  if (typeof errorPayload.details !== "object") {
    return baseMessage;
  }

  const details = errorPayload.details as {
    formErrors?: unknown;
    fieldErrors?: unknown;
    issues?: unknown;
  };

  if (Array.isArray(details.formErrors) && details.formErrors.length > 0) {
    const messages = details.formErrors.filter((value): value is string => typeof value === "string");

    if (messages.length > 0) {
      return `${baseMessage}: ${messages.join(", ")}`;
    }
  }

  if (details.fieldErrors && typeof details.fieldErrors === "object") {
    const fieldMessages = Object.entries(details.fieldErrors as Record<string, unknown>)
      .flatMap(([field, value]) =>
        Array.isArray(value)
          ? value
              .filter((entry): entry is string => typeof entry === "string")
              .map((entry) => `${field}: ${entry}`)
          : []
      )
      .filter(Boolean);

    if (fieldMessages.length > 0) {
      return `${baseMessage}: ${fieldMessages.join(" | ")}`;
    }
  }

  if (Array.isArray(details.issues) && details.issues.length > 0) {
    const issueMessages = details.issues
      .map((issue) => {
        if (!issue || typeof issue !== "object") {
          return null;
        }

        const typedIssue = issue as { path?: unknown; message?: unknown };
        const path =
          Array.isArray(typedIssue.path) && typedIssue.path.length > 0
            ? `${typedIssue.path.map(String).join(".")}: `
            : "";

        return typeof typedIssue.message === "string" ? `${path}${typedIssue.message}` : null;
      })
      .filter((value): value is string => Boolean(value));

    if (issueMessages.length > 0) {
      return `${baseMessage}: ${issueMessages.join(" | ")}`;
    }
  }

  return baseMessage;
};

const withQuery = (path: string, query?: Record<string, string | string[] | boolean | undefined>) => {
  if (!query) {
    return path;
  }

  const search = new URLSearchParams();

  for (const [key, value] of Object.entries(query)) {
    if (value === undefined || value === "") {
      continue;
    }

    if (Array.isArray(value)) {
      if (value.length > 0) {
        search.set(key, value.join(","));
      }
      continue;
    }

    search.set(key, String(value));
  }

  const suffix = search.toString();
  return suffix ? `${path}?${suffix}` : path;
};

export const readStoredSession = () => {
  if (typeof window === "undefined") {
    return null;
  }

  const raw = safeStorageGet(sessionStorageKey);

  if (!raw) {
    return null;
  }

  try {
    return JSON.parse(raw) as AuthSession;
  } catch {
    return null;
  }
};

export const saveStoredSession = (session: AuthSession) => {
  if (typeof window === "undefined") {
    return;
  }

  safeStorageSet(sessionStorageKey, JSON.stringify(session));
  dispatchSessionSync();
};

export const clearStoredSession = () => {
  if (typeof window === "undefined") {
    return;
  }

  safeStorageRemove(sessionStorageKey);
  dispatchSessionSync();
};

const apiFetch = async <T>(path: string, options?: { session?: AuthSession | null; init?: RequestInit }) => {
  const headers = new Headers(options?.init?.headers);

  if (!(options?.init?.body instanceof FormData)) {
    headers.set("Content-Type", "application/json");
  }

  if (options?.session?.token) {
    headers.set("Authorization", `Bearer ${options.session.token}`);
  }

  const response = await fetch(`${apiBaseUrl}${path}`, {
    ...options?.init,
    headers,
    cache: "no-store"
  });

  if (response.status === 204) {
    return undefined as T;
  }

  const responseText = await response.text();
  const payload = responseText
    ? (() => {
        try {
          return JSON.parse(responseText) as unknown;
        } catch {
          return {
            error: responseText
          };
        }
      })()
    : null;

  if (!response.ok) {
    if (response.status === 401 && options?.session?.token) {
      clearStoredSession();
    }

    throw new Error(formatApiError(payload));
  }

  return payload as T;
};

export const loginRequest = async (email: string, password: string) =>
  apiFetch<AuthSession>("/auth/login", {
    init: {
      method: "POST",
      body: JSON.stringify({ email, password })
    }
  });

export const getTimelineRequest = async (
  session: AuthSession,
  query: Record<string, string | string[] | boolean | undefined>
) => {
  const response = await apiFetch<TimelineResponse>(withQuery("/timeline", query), { session });
  return response;
};

export const getCategoriesRequest = async (session: AuthSession) => {
  const response = await apiFetch<{ data: EquipmentCategory[] }>("/equipment-categories", { session });
  return response.data;
};

export const getUserPreferencesRequest = async (session: AuthSession) => {
  const response = await apiFetch<{ data: UserPreferences }>("/auth/preferences", { session });
  return response.data;
};

export const updateUserPreferencesRequest = async (session: AuthSession, payload: Partial<UserPreferences>) => {
  const response = await apiFetch<{ data: UserPreferences }>("/auth/preferences", {
    session,
    init: {
      method: "PATCH",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const createCategoryRequest = async (session: AuthSession, payload: { name: string }) => {
  const response = await apiFetch<{ data: EquipmentCategory }>("/equipment-categories", {
    session,
    init: {
      method: "POST",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const updateCategoryRequest = async (session: AuthSession, id: string, payload: { name: string }) => {
  const response = await apiFetch<{ data: EquipmentCategory }>(`/equipment-categories/${id}`, {
    session,
    init: {
      method: "PATCH",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const deleteCategoryRequest = async (session: AuthSession, id: string) =>
  apiFetch<void>(`/equipment-categories/${id}`, {
    session,
    init: {
      method: "DELETE"
    }
  });

export const getItemsRequest = async (
  session: AuthSession,
  query?: Record<string, string | string[] | boolean | undefined>
) => {
  const response = await apiFetch<{ data: EquipmentItem[] }>(withQuery("/equipment-items", query), { session });
  return response.data;
};

export const createItemRequest = async (
  session: AuthSession,
  payload: Partial<EquipmentItem> & { categoryId: string; name: string; status: EquipmentItem["status"] }
) => {
  const response = await apiFetch<{ data: EquipmentItem }>("/equipment-items", {
    session,
    init: {
      method: "POST",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const updateItemRequest = async (session: AuthSession, id: string, payload: Record<string, unknown>) => {
  const response = await apiFetch<{ data: EquipmentItem }>(`/equipment-items/${id}`, {
    session,
    init: {
      method: "PATCH",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const deleteItemRequest = async (session: AuthSession, id: string) =>
  apiFetch<void>(`/equipment-items/${id}`, {
    session,
    init: {
      method: "DELETE"
    }
  });

export const getUsersRequest = async (session: AuthSession) => {
  const response = await apiFetch<{ data: UserRecord[] }>("/users", { session });
  return response.data;
};

export const createUserRequest = async (session: AuthSession, payload: Record<string, unknown>) => {
  const response = await apiFetch<{ data: UserRecord }>("/users", {
    session,
    init: {
      method: "POST",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const updateUserRequest = async (session: AuthSession, id: string, payload: Record<string, unknown>) => {
  const response = await apiFetch<{ data: UserRecord }>(`/users/${id}`, {
    session,
    init: {
      method: "PATCH",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const deleteUserRequest = async (session: AuthSession, id: string) =>
  apiFetch<void>(`/users/${id}`, {
    session,
    init: {
      method: "DELETE"
    }
  });

export const createBookingsRequest = async (session: AuthSession, payload: Record<string, unknown>) => {
  const response = await apiFetch<{ data: RentalBooking[] }>("/bookings", {
    session,
    init: {
      method: "POST",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const updateBookingRequest = async (session: AuthSession, id: string, payload: Record<string, unknown>) => {
  const response = await apiFetch<{ data: RentalBooking }>(`/bookings/${id}`, {
    session,
    init: {
      method: "PATCH",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const updateBookingScopeRequest = async (session: AuthSession, id: string, payload: Record<string, unknown>) => {
  const response = await apiFetch<{ data: RentalBooking[] }>(`/bookings/${id}/scope`, {
    session,
    init: {
      method: "PATCH",
      body: JSON.stringify(payload)
    }
  });

  return response.data;
};

export const deleteBookingRequest = async (session: AuthSession, id: string) =>
  apiFetch<void>(`/bookings/${id}`, {
    session,
    init: {
      method: "DELETE"
    }
  });
