/**
 * Wrappers around the better-auth admin plugin (and a few core better-auth
 * endpoints) for server actions that need to manage users.
 *
 * Replaces the Supabase GoTrue admin REST API (`service.auth.admin.*`) and
 * the GoTrue public REST endpoints (`/auth/v1/*`).
 *
 * Design:
 *   - `createUser` uses the public `auth.api.signUpEmail` endpoint. The
 *     better-auth admin plugin's `/admin/create-user` requires an authenticated
 *     admin session, which is awkward to set up from a server action that
 *     already has the dev_session=platform_admin cookie (dev path) or a real
 *     admin session cookie (prod path). signUpEmail works in both modes — it
 *     creates the `user` + `account` rows in Postgres, hashes the password
 *     with better-auth's scrypt parameters, and returns a session which we
 *     discard.
 *   - `setUserPassword` uses the password reset flow: `requestPasswordReset`
 *     emails the user a reset link, OR for the dev path we set the password
 *     directly via a better-auth admin endpoint that doesn't require a session
 *     (in this codebase: an internal RPC at `set_user_password` that hashes
 *     with the same scrypt parameters).
 *   - `listUsers` uses `svc-fetch` to query the `user` table JOIN `admin_users`
 *     directly. Avoids the admin plugin's `/admin/list-users` which requires
 *     a session.
 *   - `removeUser` uses `svc-fetch` to DELETE from the `user` table. The
 *     `account` and `session` tables have ON DELETE CASCADE, so this is a
 *     one-line operation.
 *   - `requestPasswordReset` is the public better-auth endpoint — no auth
 *     needed. Used by the `/forgot-password` flow.
 *
 * All return `{ data, error }` to match the convention used elsewhere in
 * the codebase.
 */

import "server-only";
import { svcFetch, svcRpc, SVC_API_URL } from "./svc-fetch";

export type AuthAdminError = { message: string };

export type CreateUserInput = {
  email: string;
  password: string;
  name: string;
};

export type AdminUser = {
  id: string;
  email: string;
  name: string | null;
  emailVerified: boolean;
  createdAt: string;
  // Joined from admin_users:
  adminUser: {
    id: string;
    role: string;
    brand_id: string | null;
    display_name: string | null;
    phone_number: string | null;
    can_manage_products: boolean | null;
    can_manage_stops: boolean | null;
    can_manage_orders: boolean | null;
    can_manage_pickup: boolean | null;
    can_manage_messages: boolean | null;
    can_manage_refunds: boolean | null;
    can_manage_users: boolean | null;
    can_manage_water_log: boolean | null;
    can_manage_reports: boolean | null;
    can_manage_settings: boolean | null;
    active: boolean;
    must_change_password: boolean;
    last_login: string | null;
  } | null;
};

export type ListUsersOpts = {
  searchValue?: string;
  limit?: number;
  offset?: number;
};

const BETTER_AUTH_URL = (() => {
  const url = process.env.BETTER_AUTH_URL;
  if (!url) {
    throw new Error("Missing BETTER_AUTH_URL. Set it in .env.local.");
  }
  return url.replace(/\/$/, "");
})();

function postgrestError(res: Response): AuthAdminError {
  return { message: `${res.status} ${res.statusText}` };
}

// ── Public endpoints (no auth required) ─────────────────────────────────────

/**
 * Create a new user with email + password. Equivalent to a self-service signup,
 * but used here by admin server actions to provision new admin/staff users.
 *
 * Returns the created user's id and email. Returns an error if the user
 * already exists (better-auth returns 422 with `USER_ALREADY_EXISTS`).
 */
export async function createUser(
  input: CreateUserInput,
): Promise<{ data: { id: string; email: string } | null; error: AuthAdminError | null }> {
  let res: Response;
  try {
    res = await fetch(`${BETTER_AUTH_URL}/api/auth/sign-up/email`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: input.email,
        password: input.password,
        name: input.name,
      }),
    });
  } catch (e) {
    return { data: null, error: { message: (e as Error).message } };
  }
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    if (text.includes("USER_ALREADY_EXISTS") || res.status === 422) {
      return { data: null, error: { message: "A user with that email already exists." } };
    }
    return { data: null, error: { message: text || postgrestError(res).message } };
  }
  const json = (await res.json()) as { user?: { id: string; email: string } };
  if (!json.user) {
    return { data: null, error: { message: "Sign-up succeeded but no user returned." } };
  }
  return { data: { id: json.user.id, email: json.user.email }, error: null };
}

/**
 * Trigger a password-reset email. The email contains a link to the
 * /auth/callback?token=... page which validates the token and sets the new
 * password via the `resetPassword` endpoint.
 */
export async function requestPasswordReset(
  input: { email: string; redirectTo: string },
): Promise<{ data: null; error: AuthAdminError | null }> {
  let res: Response;
  try {
    res = await fetch(`${BETTER_AUTH_URL}/api/auth/request-password-reset`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: input.email, redirectTo: input.redirectTo }),
    });
  } catch (e) {
    return { data: null, error: { message: (e as Error).message } };
  }
  // better-auth always returns 200 here to avoid email enumeration
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    return { data: null, error: { message: text || postgrestError(res).message } };
  }
  return { data: null, error: null };
}

// ── Admin operations (direct DB) ────────────────────────────────────────────

/**
 * List users with their admin_users row joined.
 *
 * Queries the `user` table (better-auth's user table) and LEFT JOINs the
 * `admin_users` table on `user.id = admin_users.user_id`. Returns up to
 * `limit` rows (default 50) with `offset` for pagination.
 */
export async function listUsers(
  opts: ListUsersOpts = {},
): Promise<{ data: AdminUser[] | null; error: AuthAdminError | null }> {
  const limit = opts.limit ?? 50;
  const offset = opts.offset ?? 0;
  const params = new URLSearchParams();
  params.set("select", "id,email,name,emailVerified,createdAt,admin_users(*)");
  params.set("limit", String(limit));
  params.set("offset", String(offset));
  params.set("order", "createdAt.desc");
  if (opts.searchValue) {
    params.set("email", `ilike.*${opts.searchValue}*`);
  }
  let res: Response;
  try {
    res = await svcFetch(`/rest/v1/user?${params.toString()}`, { method: "GET" });
  } catch (e) {
    return { data: null, error: { message: (e as Error).message } };
  }
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    return { data: null, error: { message: text || postgrestError(res).message } };
  }
  const rows = (await res.json()) as Array<{
    id: string;
    email: string;
    name: string | null;
    emailVerified: boolean;
    createdAt: string;
    admin_users: AdminUser["adminUser"][] | null;
  }>;
  const users: AdminUser[] = rows.map((r) => ({
    id: r.id,
    email: r.email,
    name: r.name,
    emailVerified: r.emailVerified,
    createdAt: r.createdAt,
    adminUser: r.admin_users?.[0] ?? null,
  }));
  return { data: users, error: null };
}

/**
 * Set a user's password. Calls the internal `set_user_password` RPC which
 * hashes with better-auth's scrypt parameters.
 *
 * The RPC is defined in `api/migrations/141_update_user_password_rpc.sql`
 * and is overloaded to also handle setting the user_id (i.e. it can also be
 * used to update the auth_user link on the admin_users row).
 */
export async function setUserPassword(
  userId: string,
  newPassword: string,
): Promise<{ data: null; error: AuthAdminError | null }> {
  // Better-auth's internal RPC — same one referenced in the old
  // `update_user_password` call. Defined in migration 141.
  const result = await svcRpc("update_user_password", {
    p_user_id: userId,
    p_password: newPassword,
  });
  if (result.error) {
    return { data: null, error: { message: result.error.message } };
  }
  return { data: null, error: null };
}

/**
 * Delete a user. The `account` and `session` tables have ON DELETE CASCADE
 * foreign keys, so a single DELETE on `user` cleans up the rest.
 */
export async function removeUser(
  userId: string,
): Promise<{ data: null; error: AuthAdminError | null }> {
  let res: Response;
  try {
    res = await svcFetch(`/rest/v1/user?id=eq.${encodeURIComponent(userId)}`, {
      method: "DELETE",
    });
  } catch (e) {
    return { data: null, error: { message: (e as Error).message } };
  }
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    return { data: null, error: { message: text || postgrestError(res).message } };
  }
  return { data: null, error: null };
}

/**
 * Validate a session token. Used by the dev_session middleware to check
 * that a real session exists for the platform_admin user.
 *
 * Returns the session info (user id, etc.) if valid, or an error if not.
 */
export async function validateSession(
  token: string,
): Promise<{
  data: { user: { id: string; email: string }; session: { id: string; expiresAt: string } } | null;
  error: AuthAdminError | null;
}> {
  let res: Response;
  try {
    res = await fetch(`${BETTER_AUTH_URL}/api/auth/get-session`, {
      method: "GET",
      headers: { Authorization: `Bearer ${token}` },
    });
  } catch (e) {
    return { data: null, error: { message: (e as Error).message } };
  }
  if (!res.ok) {
    return { data: null, error: { message: postgrestError(res).message } };
  }
  const json = (await res.json()) as { user?: { id: string; email: string }; session?: { id: string; expiresAt: string } };
  if (!json.user || !json.session) {
    return { data: null, error: { message: "No active session." } };
  }
  return { data: { user: json.user, session: json.session }, error: null };
}

/** Re-export for callers that need to build their own URLs. */
export const BETTER_AUTH_BASE_URL = BETTER_AUTH_URL;
export const POSTGREST_BASE_URL = SVC_API_URL;
