"use client";

import { createContext, useContext, useEffect, useMemo, useState } from "react";

import { useSessionContext } from "./session-provider";
import { safeStorageGet, safeStorageSet } from "../../lib/client-storage";

export type TimelineViewSettings = {
  dayWidth: number;
  rowHeight: number;
  labelWidth: number;
};

type ViewMode = "desktop" | "mobile";

export type TimelineViewSettingsByMode = Record<ViewMode, TimelineViewSettings>;

export const desktopViewLimits = {
  dayWidth: { min: 26, max: 64 },
  rowHeight: { min: 25, max: 45 },
  labelWidth: { min: 150, max: 320 }
} as const;

export const mobileViewLimits = {
  dayWidth: { min: 28, max: 168 },
  rowHeight: { min: 26, max: 121 },
  labelWidth: { min: 112, max: 180 }
} as const;

type ViewSettingsContextValue = {
  settings: TimelineViewSettings;
  allSettings: TimelineViewSettingsByMode;
  updateSettings: (patch: Partial<TimelineViewSettings>) => void;
  updateSettingsForMode: (mode: ViewMode, patch: Partial<TimelineViewSettings>) => void;
  resetSettings: () => void;
  resetSettingsForMode: (mode: ViewMode) => void;
};

const desktopDefaults: TimelineViewSettings = {
  dayWidth: 40,
  rowHeight: 29,
  labelWidth: 176
};

const mobileDefaults: TimelineViewSettings = {
  dayWidth: 60,
  rowHeight: 53,
  labelWidth: 124
};

const ViewSettingsContext = createContext<ViewSettingsContextValue | undefined>(undefined);

const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);

const getViewLimits = (mode: ViewMode) => (mode === "mobile" ? mobileViewLimits : desktopViewLimits);

const getDefaultSettings = (mode: ViewMode): TimelineViewSettings => (mode === "mobile" ? mobileDefaults : desktopDefaults);

const normalizeSettings = (
  candidate: Partial<TimelineViewSettings> | null | undefined,
  fallback: TimelineViewSettings,
  mode: ViewMode
): TimelineViewSettings => {
  const limits = getViewLimits(mode);
  const dayWidth = clamp(Number(candidate?.dayWidth ?? fallback.dayWidth), limits.dayWidth.min, limits.dayWidth.max);
  const rowHeight = clamp(Number(candidate?.rowHeight ?? fallback.rowHeight), limits.rowHeight.min, limits.rowHeight.max);
  const labelWidth = clamp(Number(candidate?.labelWidth ?? fallback.labelWidth), limits.labelWidth.min, limits.labelWidth.max);

  return {
    dayWidth,
    rowHeight,
    labelWidth
  };
};

const getStorageKey = (userId: string, mode: ViewMode) => `rental.timeline-view.v4.${userId}.${mode}`;

export const ViewSettingsProvider = ({ children }: { children: React.ReactNode }) => {
  const { session } = useSessionContext();
  const [settings, setSettings] = useState<TimelineViewSettings>(desktopDefaults);
  const [allSettings, setAllSettings] = useState<TimelineViewSettingsByMode>({
    desktop: desktopDefaults,
    mobile: mobileDefaults
  });
  const [hydratedUserId, setHydratedUserId] = useState<string | null>(null);
  const [viewMode, setViewMode] = useState<ViewMode>("desktop");

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

    const mediaQuery = window.matchMedia("(max-width: 1023px)");
    const updateMode = () => setViewMode(mediaQuery.matches ? "mobile" : "desktop");

    updateMode();
    mediaQuery.addEventListener("change", updateMode);
    return () => mediaQuery.removeEventListener("change", updateMode);
  }, []);

  useEffect(() => {
    if (!session?.user.id) {
      setAllSettings({
        desktop: desktopDefaults,
        mobile: mobileDefaults
      });
      setHydratedUserId(null);
      return;
    }

    if (typeof window === "undefined") {
      const fallbackSettings = {
        desktop: desktopDefaults,
        mobile: mobileDefaults
      };
      setAllSettings(fallbackSettings);
      setSettings(fallbackSettings[viewMode]);
      setHydratedUserId(session.user.id);
      return;
    }

    const nextAllSettings: TimelineViewSettingsByMode = {
      desktop: desktopDefaults,
      mobile: mobileDefaults
    };

    (["desktop", "mobile"] as ViewMode[]).forEach((mode) => {
      const fallback = getDefaultSettings(mode);
      const raw = safeStorageGet(getStorageKey(session.user.id, mode));

      if (!raw) {
        nextAllSettings[mode] = fallback;
        return;
      }

      try {
        nextAllSettings[mode] = normalizeSettings(JSON.parse(raw) as Partial<TimelineViewSettings>, fallback, mode);
      } catch {
        nextAllSettings[mode] = fallback;
      }
    });

    setAllSettings(nextAllSettings);
    setSettings(nextAllSettings[viewMode]);
    setHydratedUserId(session.user.id);
  }, [session?.user.id, viewMode]);

  useEffect(() => {
    if (!session?.user.id || hydratedUserId !== session.user.id || typeof window === "undefined") {
      return;
    }

    (["desktop", "mobile"] as ViewMode[]).forEach((mode) => {
      safeStorageSet(getStorageKey(session.user.id, mode), JSON.stringify(allSettings[mode]));
    });
  }, [allSettings, hydratedUserId, session?.user.id]);

  useEffect(() => {
    setSettings(allSettings[viewMode]);
  }, [allSettings, viewMode]);

  const value = useMemo<ViewSettingsContextValue>(
    () => ({
      settings,
      allSettings,
      updateSettings: (patch) => {
        setAllSettings((current) => {
          const nextForMode = normalizeSettings({ ...current[viewMode], ...patch }, current[viewMode], viewMode);
          return {
            ...current,
            [viewMode]: nextForMode
          };
        });
      },
      updateSettingsForMode: (mode, patch) => {
        setAllSettings((current) => {
          const nextForMode = normalizeSettings({ ...current[mode], ...patch }, current[mode], mode);
          return {
            ...current,
            [mode]: nextForMode
          };
        });
      },
      resetSettings: () => {
        setAllSettings((current) => ({
          ...current,
          [viewMode]: getDefaultSettings(viewMode)
        }));
      },
      resetSettingsForMode: (mode) => {
        setAllSettings((current) => ({
          ...current,
          [mode]: getDefaultSettings(mode)
        }));
      }
    }),
    [allSettings, settings, viewMode]
  );

  return <ViewSettingsContext.Provider value={value}>{children}</ViewSettingsContext.Provider>;
};

export const useViewSettings = () => {
  const context = useContext(ViewSettingsContext);

  if (!context) {
    throw new Error("useViewSettings must be used within ViewSettingsProvider");
  }

  return context;
};
