/**
 * Water Log — auth layer.
 *
 * Centralizes the auth gates the water-log module uses:
 *
 *   1. `requireFieldSession()`        — read wl_session cookie, look up
 *                                        the session row in `water_sessions`,
 *                                        return user/brand/role. Gates every
 *                                        irrigator PIN action (/water).
 *   2. `requireWaterAdminPermission()`— site-admin gate (Neon Auth +
 *                                        admin_users.can_manage_water_log).
 *                                        Gates /admin/water-log/*.
 *
 * **Crucially, the PIN-only helper (1) never calls `getSession()` from
 * `@/lib/auth` and never calls `getAdminUser()`.** The water log
 * module is intentionally PIN-based and self-contained; a prior
 * version of this code carried spurious `getSession()` calls that
 * hung PIN submission for users with no platform login. The static
 * "does not import getSession" test in
 * `tests/unit/water-log-auth.test.ts` guards against that regression.
 *
 * NOTE: Brand-admin PIN auth for `/water/admin/*` lives in
 * `src/lib/water-admin-pin-auth.ts` (cookie-only, no DB session,
 * no Neon Auth). It is intentionally a separate module so it can be
 * audited in isolation.
 */
"use server";

import { cookies } from "next/headers";
import { eq } from "drizzle-orm";
import { withPlatformAdmin } from "@/db/client";
import {
  waterSessions,
  waterIrrigators,
} from "@/db/schema/water-log";
import { getAdminUser } from "@/lib/admin-permissions";
import {
  WL_SESSION_COOKIE,
  type FieldSession,
  type FieldSessionResult,
} from "@/lib/water-log/session-server";

// ── Result types ────────────────────────────────────────────────────────

// `FieldSession` is now defined in `@/lib/water-log/session.ts` so the
// `FieldSession | { ok: false, error }` shape is shared with
// `lib/water-log/pin-login.ts` (Phase 2) and the rest of the module
// surface. Re-export for back-compat with existing call sites.
export type { FieldSession };

export type SiteAdminPermission =
  | { ok: true; adminUser: NonNullable<Awaited<ReturnType<typeof getAdminUser>>> }
  | { ok: false; error: string };

// ── Helpers ─────────────────────────────────────────────────────────────

/**
 * Field session gate — /water (irrigator PIN).
 *
 * Reads the `wl_session` cookie, joins it to `water_sessions` and the
 * linked `water_irrigators` row, and returns the user/brand/role.
 *
 * Returns `{ ok: false, error }` for:
 *   - missing cookie                   → "Not logged in"
 *   - cookie with no matching row      → "Session not found"
 *   - row past `expires_at`            → "Session expired"  (best-effort
 *                                        cleanup of the stale row)
 *   - irrigator marked `active = false`→ "User is inactive"
 *
 * Never calls `getSession()`. Never calls `getAdminUser()`. The only
 * network round-trip is the local DB query.
 */
export async function requireFieldSession(): Promise<FieldSessionResult> {
  const cookieStore = await cookies();
  const sessionId = cookieStore.get(WL_SESSION_COOKIE)?.value;
  if (!sessionId) return { ok: false, error: "Not logged in" };

  return withPlatformAdmin(async (db) => {
    const rows = await db
      .select({
        session: waterSessions,
        irrigator: waterIrrigators,
      })
      .from(waterSessions)
      .innerJoin(
        waterIrrigators,
        eq(waterIrrigators.id, waterSessions.irrigatorId),
      )
      .where(eq(waterSessions.id, sessionId))
      .limit(1);
    const row = rows[0];
    if (!row) return { ok: false as const, error: "Session not found" };
    if (row.session.expiresAt.getTime() < Date.now()) {
      // Best-effort cleanup
      try {
        await db.delete(waterSessions).where(eq(waterSessions.id, sessionId));
      } catch {
        // ignore — the row may have been deleted concurrently
      }
      return { ok: false as const, error: "Session expired" };
    }
    if (!row.irrigator.active) {
      return { ok: false as const, error: "User is inactive" };
    }
    return {
      ok: true as const,
      userId: row.irrigator.id,
      brandId: row.irrigator.brandId,
      role:
        (row.irrigator.role as "irrigator" | "water_admin") ?? "irrigator",
    };
  });
}

/**
 * Read the current field session user (or null). Cheap, idempotent.
 * Used by the admin settings page to render the "logged in as" badge
 * without forcing a hard error when the session is gone.
 */
export async function getFieldSessionUser(): Promise<{
  userId: string;
  name: string;
  brandId: string;
  role: "irrigator" | "water_admin";
} | null> {
  const s = await requireFieldSession();
  if (!s.ok) return null;
  return withPlatformAdmin(async (db) => {
    const rows = await db
      .select({
        name: waterIrrigators.name,
        role: waterIrrigators.role,
      })
      .from(waterIrrigators)
      .where(eq(waterIrrigators.id, s.userId))
      .limit(1);
    const row = rows[0];
    if (!row) return null;
    return {
      userId: s.userId,
      name: row.name,
      brandId: s.brandId,
      role: (row.role as "irrigator" | "water_admin") ?? "irrigator",
    };
  });
}

/**
 * Site-admin gate — /admin/water-log/*.
 *
 * This is the only helper in this module that calls
 * `getAdminUser()`, and that's intentional: it backs the
 * `/admin/water-log/*` pages which sit behind Neon Auth in the
 * middleware. The PIN-only helper above deliberately does not.
 */
export async function requireWaterAdminPermission(): Promise<SiteAdminPermission> {
  const adminUser = await getAdminUser();
  if (!adminUser) return { ok: false, error: "Not signed in" };
  if (
    !adminUser.can_manage_water_log &&
    adminUser.role !== "platform_admin"
  ) {
    return { ok: false, error: "Not authorized" };
  }
  // `adminUser` is non-null here (the first guard returned). Cast keeps
  // the success-branch type narrow (non-nullable) so callers can read
  // `auth.adminUser.foo` without an extra null check.
  return { ok: true, adminUser: adminUser as NonNullable<typeof adminUser> };
}