"use server";

import { redirect } from "next/navigation";
import { z } from "zod";

import {
  createSession,
  destroySession,
  toCurrentAdmin,
} from "@/lib/auth/session";
import { CmsError, cmsClient, cmsPaths } from "@/lib/cms/api";

export type LoginState = {
  error?: string;
  email?: string;
};

const loginSchema = z.object({
  email: z.string().trim().toLowerCase().email().max(254),
  password: z.string().min(1).max(256),
  next: z.string().max(2000).optional(),
});

/** Only same-site admin paths, so `?next=` can't become an open redirect. */
function safeRedirectPath(next: string | undefined) {
  if (!next || !next.startsWith("/admin") || next.startsWith("//")) {
    return "/admin";
  }
  return next;
}

/**
 * Signs in through the Laravel CMS (`POST admin/auth/login`). Laravel checks
 * the password, rate limits attempts and refuses inactive accounts.
 */
export async function login(
  _previous: LoginState,
  formData: FormData,
): Promise<LoginState> {
  const parsed = loginSchema.safeParse({
    email: formData.get("email"),
    password: formData.get("password"),
    next: formData.get("next") || undefined,
  });
  const email = String(formData.get("email") ?? "").slice(0, 254);
  if (!parsed.success) {
    return { error: "Enter a valid email and password.", email };
  }

  let token: string;
  try {
    const cms = await cmsClient({ auth: false });
    const body = await cms.request<{ token?: string }>(cmsPaths.login, {
      json: { email: parsed.data.email, password: parsed.data.password },
      auth: false,
    });
    if (!body.token || !toCurrentAdmin(body)) {
      console.error("[auth] login answered without a token or user");
      return {
        error: "The CMS didn't accept the sign-in. Please try again.",
        email,
      };
    }
    token = body.token;
  } catch (error) {
    if (error instanceof CmsError) {
      if (error.status === 429) {
        return {
          error: error.message || "Too many sign-in attempts. Try again later.",
          email,
        };
      }
      if (
        error.status === 401 ||
        error.status === 403 ||
        error.status === 422
      ) {
        // One message for every failure, so the form doesn't reveal which accounts exist.
        return { error: "Incorrect email or password.", email };
      }
    }
    console.error("[auth] login failed", error);
    return {
      error: "Couldn't reach the CMS. Please try again in a moment.",
      email,
    };
  }

  await createSession(token);
  redirect(safeRedirectPath(parsed.data.next));
}

export async function logout() {
  await destroySession();
  redirect("/login");
}
