/**
 * useAddDealEffects — side-effect bundle for AddDealShell.
 *
 * Owns:
 * - draft resume from `?draftId=...` query
 * - PhonePreview chunk eager-preload (requestIdleCallback)
 * - dealState fetch (edit mode)
 * - categories + tags fetches keyed off `dealType` and `categoryId`
 * - image preview sync from form's imageSet
 *
 * Returns the small pieces of state these effects expose for the shell to render.
 */
import { useEffect, useState } from 'react';
import type { UseFormReturn } from 'react-hook-form';
import { useWatch } from 'react-hook-form';
import type { CategoryItem } from '@/components/ui/domain/CategoryPillGroup';
import type { TagItem } from '@/components/ui/domain/TagPillGroup';
import type { LaunchMode } from '@/components/ui/primitives/LaunchModePicker';
import { buildVariantUrl } from '@/components/ui/primitives/Image/buildVariantUrl';
import { captureCaught } from '@/lib/observability';
import { fetchWithRefresh } from '@/lib/api/refresh-on-401';
import type { AddDealFormValues } from './useAddDeal';
import { toIsoWithOffset } from './useAddDeal';

/** Internal: lazy-import the PhonePreview chunk for eager preload only. */
function importPhonePreviewChunk() {
  return import('@/components/ui/domain/vendor/PhonePreview');
}

export interface UseAddDealEffectsOptions {
  form: UseFormReturn<AddDealFormValues>;
  isEditMode: boolean;
  dealId: string | undefined;
  initialDealState: string | undefined;
  initialDraftId: string | null;
  /** Error i18n function for category/tag load failures. */
  onCategoriesError: (msg: string) => void;
  onTagsError: (msg: string) => void;
  errorMessages: { categories: string; tags: string };
}

export interface UseAddDealEffectsResult {
  categories: CategoryItem[];
  tags: TagItem[];
  dealImagePreviewUrl: string | null;
  setDealImagePreviewUrl: (url: string | null) => void;
  currentDealState: string | null;
  starred: boolean;
  setStarred: (v: boolean) => void;
  launchMode: LaunchMode;
  setLaunchMode: (m: LaunchMode) => void;
}

export function useAddDealEffects({
  form,
  isEditMode,
  dealId,
  initialDealState,
  initialDraftId,
  errorMessages,
}: UseAddDealEffectsOptions): UseAddDealEffectsResult {
  const [categories, setCategories] = useState<CategoryItem[]>([]);
  const [tags, setTags] = useState<TagItem[]>([]);
  const [dealImagePreviewUrl, setDealImagePreviewUrl] = useState<string | null>(null);
  const [currentDealState, setCurrentDealState] = useState<string | null>(initialDealState ?? null);
  const [starred, setStarred] = useState(false);
  const [launchMode, setLaunchMode] = useState<LaunchMode>('now');

  const watchedDealType = useWatch({ control: form.control, name: 'dealType' });
  const watchedImageSet = useWatch({ control: form.control, name: 'imageSet' });
  const watchedCategoryId = useWatch({ control: form.control, name: 'categoryId' });

  // Eager-preload PhonePreview chunk during browser idle.
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const ric = (
      window as Window &
        typeof globalThis & {
          requestIdleCallback?: (cb: () => void) => number;
          cancelIdleCallback?: (id: number) => void;
        }
    ).requestIdleCallback;
    if (typeof ric === 'function') {
      const id = ric(() => {
        void importPhonePreviewChunk();
      });
      const cancel = (
        window as Window &
          typeof globalThis & {
            cancelIdleCallback?: (id: number) => void;
          }
      ).cancelIdleCallback;
      return () => {
        if (typeof cancel === 'function') cancel(id);
      };
    }
    const timer = setTimeout(() => {
      void importPhonePreviewChunk();
    }, 1);
    return () => clearTimeout(timer);
  }, []);

  // Resume from draftId on mount.
  useEffect(() => {
    if (!initialDraftId || dealId) return;
    let cancelled = false;
    fetchWithRefresh(`/api/vendor/deals/drafts/${initialDraftId}`, { credentials: 'same-origin' })
      .then((r) => {
        if (r.status === 404 || r.status === 403) {
          const url = new URL(window.location.href);
          url.searchParams.delete('draftId');
          window.history.replaceState({}, '', url.toString());
          return null;
        }
        return r.json() as Promise<{
          draft?: { payload?: Record<string, unknown>; starred?: boolean };
        }>;
      })
      .then((data) => {
        if (cancelled || !data?.draft?.payload) return;
        const stored = data.draft.payload as Record<string, unknown>;
        const sliceLocal = (v: unknown) =>
          typeof v === 'string' && v.length >= 16 ? v.slice(0, 16) : v;
        const rawStart = sliceLocal(stored.windowStart) as string | undefined;

        const fiveMinAgo = Date.now() - 5 * 60 * 1000;
        let resolvedStart: string | undefined = rawStart;
        let resolvedMode: LaunchMode = 'now';
        if (rawStart) {
          const parsed = new Date(rawStart).getTime();
          if (!Number.isNaN(parsed) && parsed >= fiveMinAgo + 5 * 60 * 1000) {
            resolvedMode = 'scheduled';
          } else {
            resolvedStart = undefined;
          }
        }
        setLaunchMode(resolvedMode);

        const normalized: Record<string, unknown> = {
          ...stored,
          windowStart: resolvedStart,
          windowEnd: sliceLocal(stored.windowEnd),
        };
        const defined = Object.fromEntries(
          Object.entries(normalized).filter(([, v]) => v !== undefined && v !== null),
        );
        form.reset({ ...form.getValues(), ...defined } as Partial<AddDealFormValues>);
        if (data.draft.starred) setStarred(true);
        const imageSet = stored.imageSet as { deal?: Array<{ r2Key: string }> } | undefined;
        const primaryKey = imageSet?.deal?.[0]?.r2Key;
        if (primaryKey) {
          setDealImagePreviewUrl(buildVariantUrl(primaryKey, 'card', 400));
        }
      })
      .catch((err) => {
        captureCaught(err, { scope: 'features.vendor-add-deal.AddDeal', severity: 'info' });
      });
    return () => {
      cancelled = true;
    };
  }, [initialDraftId, dealId, form]);

  // Fetch dealState for edit mode (drives Archive button visibility).
  useEffect(() => {
    if (!isEditMode || !dealId) return;
    let cancelled = false;
    fetchWithRefresh(`/api/vendor/deals/${dealId}`)
      .then((r) => r.json() as Promise<{ ok: boolean; deal?: { dealState?: string } }>)
      .then(({ ok, deal }) => {
        if (!ok || cancelled || !deal) return;
        if (typeof deal.dealState === 'string') setCurrentDealState(deal.dealState);
      })
      .catch((err: unknown) => {
        captureCaught(err, { scope: 'AddDeal.fetchDealState', severity: 'info' });
      });
    return () => {
      cancelled = true;
    };
  }, [isEditMode, dealId]);

  // Sync image preview from form's imageSet (edit mode).
  // Defer the setState into a microtask so react-hooks/set-state-in-effect
  // is satisfied — the inner async boundary makes it event-like rather than
  // a synchronous render-coupled write.
  useEffect(() => {
    if (dealImagePreviewUrl) return;
    const primaryKey = watchedImageSet?.deal?.[0]?.r2Key;
    if (!primaryKey) return;
    const next = buildVariantUrl(primaryKey, 'card', 400);
    let cancelled = false;
    void Promise.resolve().then(() => {
      if (cancelled) return;
      setDealImagePreviewUrl(next);
    });
    return () => {
      cancelled = true;
    };
  }, [watchedImageSet, dealImagePreviewUrl]);

  // Reload categories when dealType changes.
  useEffect(() => {
    if (!watchedDealType) return;
    form.setValue('categoryId', undefined);
    let cancelled = false;
    fetchWithRefresh(`/api/deals/categories?dealType=${watchedDealType}`)
      .then((r) => r.json())
      .then((raw: unknown) => {
        if (cancelled) return;
        const data = raw as { categories?: CategoryItem[] };
        setCategories(data.categories ?? []);
      })
      .catch((err) => {
        if (cancelled) return;
        captureCaught(err, {
          scope: 'features.vendor-add-deal.AddDeal.categories',
          severity: 'warning',
        });
        form.setError('root', { message: errorMessages.categories });
      });
    return () => {
      cancelled = true;
    };
  }, [watchedDealType, form, errorMessages.categories]);

  // Re-fetch tags when category changes.
  useEffect(() => {
    let cancelled = false;
    if (!watchedCategoryId) {
      // Defer reset into a microtask so the setter is no longer a
      // synchronous render-coupled write (satisfies react-hooks/set-state-in-effect).
      void Promise.resolve().then(() => {
        if (!cancelled) setTags([]);
      });
      return () => {
        cancelled = true;
      };
    }
    const url = `/api/deals/tags?all=1&categoryId=${watchedCategoryId}`;
    fetchWithRefresh(url)
      .then((r) => r.json())
      .then((raw: unknown) => {
        const data = raw as { tags?: TagItem[] };
        if (!cancelled) setTags(data.tags ?? []);
      })
      .catch((err) => {
        if (cancelled) return;
        captureCaught(err, {
          scope: 'features.vendor-add-deal.AddDeal.tags',
          severity: 'warning',
        });
        form.setError('root', { message: errorMessages.tags });
      });
    return () => {
      cancelled = true;
    };
  }, [errorMessages.tags, form, watchedCategoryId]);

  return {
    categories,
    tags,
    dealImagePreviewUrl,
    setDealImagePreviewUrl,
    currentDealState,
    starred,
    setStarred,
    launchMode,
    setLaunchMode,
  };
}

/** Build the payload shape the autosave endpoint expects from current form values. */
export function buildAutosavePayload(
  values: AddDealFormValues,
  calculatedPrice: string | null | undefined,
): Record<string, unknown> {
  const { quantity, ...rest } = values;
  return {
    ...(rest as Record<string, unknown>),
    quantityTotal: quantity,
    discountedPrice: calculatedPrice ?? undefined,
    windowStart: toIsoWithOffset(values.windowStart),
    windowEnd: toIsoWithOffset(values.windowEnd),
  };
}
