"use client";

/**
 * MobileWaterApp — state machine + screen switcher for the mobile
 * Tuxedo Water Log experience.
 *
 * Flow:
 *   pin ─verify─▶ headgates ─pick─▶ log ─submit─▶ success
 *    ▲              │                                       │
 *    │              └───────────────(logout)─────────────────┤
 *    └─(logout)─────(anywhere)──────────────────────────────┘
 *                                                  │
 *        success ── Log Another ─▶ log (cleared) ─┘
 *        success ── Back to Headgates ─▶ headgates
 *
 * Hydration:
 *   - On mount we peek at `document.cookie` for the `wl_session`
 *     cookie (set by the server action `verifyWaterPin`). If it's
 *     present we skip the PIN screen and load headgates directly.
 *   - This mirrors the existing field-client behavior — irrigators
 *     who already signed in once in their shift shouldn't have to
 *     re-enter their PIN every time they reopen the page.
 *
 * Server actions used:
 *   - `verifyWaterPin`        → signs the irrigator in, sets cookie
 *   - `getWaterHeadgates`     → loads the list of active gates
 *   - `submitWaterEntry`      → posts a new reading
 *   - `logoutWater`           → clears the cookie + session row
 */
import { useCallback, useEffect, useState } from "react";
import {
  verifyWaterPin,
  submitWaterEntry,
  getWaterHeadgates,
  logoutWater,
} from "@/actions/water-log/field";
import type { FieldHeadgate, Screen, SubmittedLog } from "./types";
import { MobileWaterPinScreen } from "./PinScreen";
import { MobileWaterHeadgateList } from "./HeadgateList";
import { MobileWaterLogForm } from "./LogForm";
import { MobileWaterSuccessScreen } from "./SuccessScreen";
import { WL_SESSION_COOKIE } from "@/lib/water-log/session";
import { TUXEDO_BRAND_ID } from "@/lib/water-log/brand";
import { useLang } from "@/lib/water-log/lang-context";

// Server-side auth errors that should bounce the user to the PIN
// screen rather than surface as a read error. Matched against the
// exact strings returned by `requireFieldSession` in
// src/actions/water-log/auth.ts.
const SESSION_ERRORS = [
  "Session expired",
  "Session not found",
  "User is inactive",
  "Not logged in",
];

export function MobileWaterApp() {
  const [screen, setScreen] = useState<Screen>("pin");
  // `useLang()` reads from the <LangProvider> in WaterFieldClient,
  // so it's always up-to-date when the user toggles via the toggle
  // in the headgate list header.
  const { t } = useLang();
  const [irrigatorName, setIrrigatorName] = useState<string | null>(null);
  const [headgates, setHeadgates] = useState<FieldHeadgate[]>([]);
  const [loadingHeadgates, setLoadingHeadgates] = useState(false);
  const [headgateError, setHeadgateError] = useState<string | null>(null);
  const [selectedHeadgate, setSelectedHeadgate] =
    useState<FieldHeadgate | null>(null);
  const [submittedLog, setSubmittedLog] = useState<SubmittedLog | null>(null);
  // Monotonic counter used to remount the log form on "Log Another"
  // (so the input + notes clear deterministically without an
  // impure Date.now() call in render).
  const [logFormKey, setLogFormKey] = useState(0);

  // ── Headgate fetch (shared by initial load + pull-to-refresh) ──
  const loadHeadgates = useCallback(async () => {
    setLoadingHeadgates(true);
    setHeadgateError(null);
    try {
      const hgs = await getWaterHeadgates(TUXEDO_BRAND_ID, true);
      setHeadgates(hgs.filter((h) => h.active));
    } catch (err) {
      setHeadgateError(
        err instanceof Error ? err.message : t.headgates.loadError,
      );
    } finally {
      setLoadingHeadgates(false);
    }
  }, [t.headgates.loadError]);

  // ── Initial bootstrap: if `wl_session` exists, jump straight to
  //    the headgate list. We defer the cookie read into an effect
  //    so SSR markup matches (avoids hydration warnings). ────────
  useEffect(() => {
    if (typeof document === "undefined") return;
    const hasSession = document.cookie
      .split(";")
      .some((c) => c.trim().startsWith(`${WL_SESSION_COOKIE}=`));
    if (!hasSession) return;
    // Wrap setScreen in a microtask so the call doesn't fire
    // synchronously inside the effect body.
    queueMicrotask(() => {
      setScreen("headgates");
      void loadHeadgates();
    });
  }, [loadHeadgates]);

  // ── PIN submit ────────────────────────────────────────────────
  const handlePinSubmit = useCallback(
    async (pin: string) => {
      const result = await verifyWaterPin(TUXEDO_BRAND_ID, pin);
      if (!result.success) {
        // Trigger the shake animation on the PIN screen.
        if (typeof window !== "undefined") {
          window.dispatchEvent(new Event("water-pin-shake"));
        }
        throw new Error(result.error);
      }
      // If the user turns out to be a water_admin, the legacy flow
      // bounces them to /water/admin. Preserve that for parity.
      if (result.role === "water_admin") {
        window.location.href = "/water/admin";
        return;
      }
      setIrrigatorName(result.name ?? null);
      setScreen("headgates");
      void loadHeadgates();
    },
    [loadHeadgates],
  );

  // ── Headgate select ───────────────────────────────────────────
  const handleHeadgateSelect = useCallback((hg: FieldHeadgate) => {
    setSelectedHeadgate(hg);
    setScreen("log");
  }, []);

  // ── Submit entry ──────────────────────────────────────────────
  const handleLogSubmit = useCallback(
    async (input: { measurement: number; unit: string; notes: string }) => {
      if (!selectedHeadgate) {
        throw new Error(t.log.noHeadgateSelected);
      }
      const result = await submitWaterEntry(
        selectedHeadgate.id,
        input.measurement,
        input.unit,
        input.notes,
      );
      if (!result.success) {
        if (SESSION_ERRORS.includes(result.error)) {
          setScreen("pin");
          throw new Error(t.log.sessionExpired);
        }
        throw new Error(t.log.submitError);
      }
      setSubmittedLog({
        headgateName: selectedHeadgate.name,
        measurement: input.measurement,
        unit: input.unit,
        timestamp: new Date(),
      });
      setScreen("success");
    },
    [selectedHeadgate, t.log.noHeadgateSelected, t.log.sessionExpired, t.log.submitError],
  );

  // ── Success actions ───────────────────────────────────────────
  const handleLogAnother = useCallback(() => {
    // Stay on the same headgate but remount the form so the input
    // + notes clear. Bumping the state key is deterministic —
    // never call Date.now() (or any impure fn) during render.
    setLogFormKey((k) => k + 1);
    setSubmittedLog(null);
    setScreen("log");
  }, []);

  const handleBackToHeadgates = useCallback(() => {
    setSubmittedLog(null);
    setSelectedHeadgate(null);
    setScreen("headgates");
    void loadHeadgates();
  }, [loadHeadgates]);

  // ── Logout (from anywhere) ────────────────────────────────────
  const handleLogout = useCallback(async () => {
    try {
      await logoutWater();
    } catch {
      // Even if the server logout fails, clear local state so the
      // user isn't stuck.
    }
    setIrrigatorName(null);
    setHeadgates([]);
    setSelectedHeadgate(null);
    setSubmittedLog(null);
    setScreen("pin");
  }, []);

  // ── Switch on the current screen ──────────────────────────────
  // Compute the screen element first, then wrap it once in
  // <LangProvider>. Wrapping per-case would remount the provider on
  // every screen change, wiping lang state.
  let screenElement: React.ReactNode = null;
  switch (screen) {
    case "pin":
      screenElement = <MobileWaterPinScreen onSubmit={handlePinSubmit} />;
      break;

    case "headgates":
      screenElement = (
        <MobileWaterHeadgateList
          irrigatorName={irrigatorName ?? undefined}
          headgates={headgates}
          loading={loadingHeadgates}
          error={headgateError}
          onSelect={handleHeadgateSelect}
          onRefresh={loadHeadgates}
          onLogout={handleLogout}
        />
      );
      break;

    case "log":
      if (!selectedHeadgate) {
        // Defensive: shouldn't happen, but bounce back to list.
        queueMicrotask(() => setScreen("headgates"));
        screenElement = null;
        break;
      }
      screenElement = (
        <MobileWaterLogForm
          key={`${selectedHeadgate.id}-${logFormKey}`}
          headgate={selectedHeadgate}
          irrigatorName={irrigatorName ?? undefined}
          onSubmit={handleLogSubmit}
          onChangeHeadgate={handleBackToHeadgates}
          onLogout={handleLogout}
        />
      );
      break;

    case "success":
      if (!submittedLog) {
        queueMicrotask(() => setScreen("headgates"));
        screenElement = null;
        break;
      }
      screenElement = (
        <MobileWaterSuccessScreen
          headgateName={submittedLog.headgateName}
          measurement={submittedLog.measurement}
          unit={submittedLog.unit}
          timestamp={submittedLog.timestamp}
          irrigatorName={irrigatorName ?? undefined}
          onLogAnother={handleLogAnother}
          onBackToHeadgates={handleBackToHeadgates}
        />
      );
      break;
  }

  // `initialLang` is computed by the parent <LangProvider> in
  // WaterFieldClient — we just return the screen element here.
  return screenElement;
}

export default MobileWaterApp;