"use client";

import {
  createContext,
  ReactNode,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";
import { Package, PaymentAccount } from "./types";
import { useAuth } from "./auth/auth-provider";
import { submitOrder, OrderSubmissionError } from "./data/orders";
import {
  saveDraftMeta,
  loadDraftMeta,
  clearDraftMeta,
  saveDraftScreenshot,
  loadDraftScreenshot,
  clearDraftScreenshot,
} from "./order-draft-storage";

// Holds the in-progress order as the customer moves through
// Buy -> Payment Methods -> Upload Screenshot. Submission talks to the
// MySQL-backed API for real (see lib/data/orders.ts) — this context is
// the scratch pad that carries the selected package/payment
// method/screenshot across those three screens, since they don't
// share a single form.
//
// Persisted across a refresh or a suspended/backgrounded mobile tab
// (see lib/order-draft-storage.ts) — this used to be a plain
// useState, which was the root cause of the "Something's missing from
// your order" regression. Hydration from storage is async (IndexedDB
// for the screenshot file), so `hydrated` is exposed for consuming
// pages to wait on before deciding "there's genuinely no draft, send
// them back to /packages" — checking that before hydration finishes
// would otherwise bounce someone with a perfectly good persisted
// draft, defeating the point.

interface DraftOrder {
  pkg: Package | null;
  phone: string;
  paymentMethod: PaymentAccount | null;
  txnId: string;
  screenshotFile: File | null;
  screenshotPreviewUrl: string | null;
}

const emptyDraft: DraftOrder = {
  pkg: null,
  phone: "",
  paymentMethod: null,
  txnId: "",
  screenshotFile: null,
  screenshotPreviewUrl: null,
};

interface OrderStoreValue {
  draft: DraftOrder;
  /** True once the initial load from persisted storage has finished (or found nothing to load) — see this file's top comment. */
  hydrated: boolean;
  setDraft: (patch: Partial<DraftOrder>) => void;
  resetDraft: () => void;
  /**
   * `overrides` lets the caller pass values straight from local component
   * state (e.g. the transaction ID the person just typed) instead of only
   * reading `draft`. This matters because `setDraft(...)` is async — a
   * caller that does `setDraft({ txnId }); await submitDraft();` in the
   * same handler would otherwise submit against the *previous* render's
   * `draft.txnId`, since this closure is captured per-render and the
   * state update hasn't flushed yet. Passing the value explicitly avoids
   * that race entirely rather than relying on timing.
   */
  submitDraft: (
    overrides?: Partial<Pick<DraftOrder, "txnId" | "phone">>
  ) => Promise<{ id: string; orderNumber: string }>;
}

const OrderStoreContext = createContext<OrderStoreValue | null>(null);

export function OrderStoreProvider({ children }: { children: ReactNode }) {
  const [draft, setDraftState] = useState<DraftOrder>(emptyDraft);
  const [hydrated, setHydrated] = useState(false);
  const { user } = useAuth();

  // Runs once on mount. Loads whatever was persisted from a previous
  // visit to this same tab (see lib/order-draft-storage.ts) — most of
  // the time that's nothing (a fresh visit), in which case this is a
  // no-op other than flipping `hydrated`.
  useEffect(() => {
    let active = true;
    (async () => {
      const meta = loadDraftMeta();
      const screenshotFile = await loadDraftScreenshot();
      if (!active) return;
      if (meta || screenshotFile) {
        setDraftState((prev) => ({
          ...prev,
          ...(meta ?? {}),
          screenshotFile,
          screenshotPreviewUrl: screenshotFile ? URL.createObjectURL(screenshotFile) : null,
        }));
      }
      setHydrated(true);
    })();
    return () => {
      active = false;
    };
  }, []);

  const value = useMemo<OrderStoreValue>(
    () => ({
      draft,
      hydrated,
      setDraft: (patch) => {
        setDraftState((prev) => {
          const next = { ...prev, ...patch };
          saveDraftMeta({ pkg: next.pkg, phone: next.phone, paymentMethod: next.paymentMethod, txnId: next.txnId });
          if ("screenshotFile" in patch) {
            if (patch.screenshotFile) {
              saveDraftScreenshot(patch.screenshotFile);
            } else {
              clearDraftScreenshot();
            }
          }
          return next;
        });
      },
      resetDraft: () => {
        setDraftState(emptyDraft);
        clearDraftMeta();
        clearDraftScreenshot();
      },
      submitDraft: async (overrides) => {
        const txnId = overrides?.txnId ?? draft.txnId;
        const phone = overrides?.phone ?? draft.phone;

        if (!user) {
          throw new OrderSubmissionError("You must be signed in.", "AUTH_REQUIRED");
        }
        if (!draft.pkg || !draft.paymentMethod || !txnId || !draft.screenshotFile) {
          throw new OrderSubmissionError("Order details are incomplete.", "ORDER_INCOMPLETE");
        }

        const result = await submitOrder({
          packageDbId: draft.pkg.dbId,
          activationPhone: phone,
          paymentMethodDbId: draft.paymentMethod.dbId,
          referenceNumber: txnId,
          screenshotFile: draft.screenshotFile,
        });

        setDraftState(emptyDraft);
        clearDraftMeta();
        clearDraftScreenshot();
        return result;
      },
    }),
    [draft, hydrated, user]
  );

  return (
    <OrderStoreContext.Provider value={value}>
      {children}
    </OrderStoreContext.Provider>
  );
}

export function useOrderStore() {
  const ctx = useContext(OrderStoreContext);
  if (!ctx) {
    throw new Error("useOrderStore must be used within OrderStoreProvider");
  }
  return ctx;
}
