"use client";

// ---------------------------------------------------------------------------
// useKratosFlow — drives the browser self-service flow lifecycle for a page
// ---------------------------------------------------------------------------
// 1. Reads `?flow=<id>` from the URL.
// 2. If absent, hard-navigates the browser to Kratos to initialize the flow
//    (Kratos sets the flow cookie and redirects back with `?flow=<id>`).
// 3. If present, fetches the flow definition; on 403/410 (expired/missing
//    cookie) it re-initializes.
// ---------------------------------------------------------------------------

import { useCallback, useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";

import { getFlow, startFlow } from "./client";
import type { FlowType, KratosFlow } from "./types";

interface UseKratosFlowResult {
  flow: KratosFlow | null;
  /** True while initializing / fetching the flow. */
  loading: boolean;
  /** Non-Kratos error (e.g. network failure). */
  error: string | null;
  /** Replace the current flow object (after a submit returns a fresh flow). */
  setFlow: (flow: KratosFlow) => void;
  /** Re-initialize the flow from scratch. */
  reinit: () => void;
}

export function useKratosFlow(
  type: FlowType,
  returnTo?: string,
): UseKratosFlowResult {
  const searchParams = useSearchParams();
  const flowId = searchParams.get("flow");

  const [flow, setFlow] = useState<KratosFlow | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const reinit = useCallback(() => {
    startFlow(type, returnTo);
  }, [type, returnTo]);

  useEffect(() => {
    let cancelled = false;

    // No flow id in the URL -> kick off a fresh flow (hard navigation).
    if (!flowId) {
      startFlow(type, returnTo);
      return;
    }

    // Fetch the flow definition. State is only mutated from the async
    // callbacks (never synchronously in the effect body) so the initial
    // `loading: true` covers the in-flight window.
    getFlow(type, flowId)
      .then((f) => {
        if (!cancelled) {
          setFlow(f);
          setError(null);
          setLoading(false);
        }
      })
      .catch((err) => {
        if (cancelled) return;
        // 403/410: the flow expired or the flow cookie is gone — re-init.
        if (
          typeof err === "object" &&
          err !== null &&
          "status" in err &&
          (err.status === 403 || err.status === 410 || err.status === 404)
        ) {
          startFlow(type, returnTo);
          return;
        }
        setError("Could not load the form. Please try again.");
        setLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, [flowId, type, returnTo]);

  return { flow, loading, error, setFlow, reinit };
}
