import { randomBytes } from "node:crypto";

import type { FastifyReply } from "fastify";

import { AppError } from "./errors.js";
import { clearSessionCookies, setSessionCookies } from "./session.js";
import { createAuthClient, supabaseAdmin } from "./supabase.js";
import { uniqueSlug } from "./utils.js";

export async function bootstrapUser(email: string, password: string, fullName: string, organizationName: string) {
  const normalizedEmail = email.trim().toLowerCase();

  const existing = await supabaseAdmin
    .from("user_profiles")
    .select("id")
    .eq("email", normalizedEmail)
    .maybeSingle();

  if (existing.data) {
    throw new AppError(409, "EMAIL_ALREADY_EXISTS", "An account already exists for this email address.");
  }

  const createdUser = await supabaseAdmin.auth.admin.createUser({
    email: normalizedEmail,
    password,
    email_confirm: true,
    user_metadata: { full_name: fullName },
  });

  if (createdUser.error || !createdUser.data.user) {
    throw new AppError(400, "SIGNUP_FAILED", createdUser.error?.message ?? "Unable to create user.");
  }

  const authUser = createdUser.data.user;
  const { data: organization, error: organizationError } = await supabaseAdmin
    .from("organizations")
    .insert({
      name: organizationName.trim(),
      slug: uniqueSlug(organizationName),
    })
    .select("*")
    .single();

  if (organizationError || !organization) {
    await supabaseAdmin.auth.admin.deleteUser(authUser.id);
    throw new AppError(500, "ORGANIZATION_CREATE_FAILED", organizationError?.message ?? "Unable to create organization.");
  }

  const { error: profileError } = await supabaseAdmin.from("user_profiles").insert({
    auth_user_id: authUser.id,
    email: normalizedEmail,
    full_name: fullName.trim(),
    role: "user",
    default_organization_id: organization.id,
  });

  if (profileError) {
    throw new AppError(500, "PROFILE_CREATE_FAILED", profileError.message);
  }

  await supabaseAdmin.from("organization_members").insert({
    organization_id: organization.id,
    auth_user_id: authUser.id,
    role: "owner",
  });

  const { data: freePlan } = await supabaseAdmin
    .from("plan_definitions")
    .select("id")
    .eq("code", "free")
    .single();

  if (freePlan?.id) {
    await supabaseAdmin.from("organization_subscriptions").insert({
      organization_id: organization.id,
      plan_definition_id: freePlan.id,
      status: "active",
    });
  }

  const authClient = createAuthClient();
  const signInResult = await authClient.auth.signInWithPassword({
    email: normalizedEmail,
    password,
  });

  if (signInResult.error || !signInResult.data.session) {
    throw new AppError(500, "SIGNIN_AFTER_SIGNUP_FAILED", signInResult.error?.message ?? "Unable to start session.");
  }

  return {
    user: signInResult.data.user ?? authUser,
    session: signInResult.data.session,
    organization,
  };
}

export async function signIn(email: string, password: string) {
  const authClient = createAuthClient();
  const result = await authClient.auth.signInWithPassword({
    email: email.trim().toLowerCase(),
    password,
  });

  if (result.error || !result.data.session || !result.data.user) {
    throw new AppError(401, "INVALID_CREDENTIALS", result.error?.message ?? "Invalid credentials.");
  }

  const { data: profile, error } = await supabaseAdmin
    .from("user_profiles")
    .select("*")
    .eq("auth_user_id", result.data.user.id)
    .single();

  if (error || !profile) {
    throw new AppError(404, "PROFILE_NOT_FOUND", error?.message ?? "User profile not found.");
  }

  return {
    session: result.data.session,
    profile,
  };
}

export async function createAppSession(reply: FastifyReply, input: { session: { access_token: string; refresh_token: string; expires_in?: number }; profile?: Record<string, unknown>; user?: { email?: string | null; id: string } }, explicitOrganizationId?: string | null) {
  const organizationId =
    explicitOrganizationId ??
    (typeof input.profile?.default_organization_id === "string" ? input.profile.default_organization_id : null);
  setSessionCookies(reply, input.session, organizationId);

  return {
    id: input.user?.id ?? String(input.profile?.auth_user_id ?? ""),
    email: input.user?.email ?? String(input.profile?.email ?? ""),
    fullName: String(input.profile?.full_name ?? ""),
    defaultOrganizationId: organizationId,
    role: String(input.profile?.role ?? "user"),
  };
}

export async function issuePasswordReset(email: string, ttlMinutes: number) {
  const normalizedEmail = email.trim().toLowerCase();
  const { data: profile } = await supabaseAdmin
    .from("user_profiles")
    .select("id, auth_user_id, email, full_name")
    .eq("email", normalizedEmail)
    .maybeSingle();

  if (!profile) {
    return null;
  }

  const token = randomBytes(24).toString("hex");
  const expiresAt = new Date(Date.now() + ttlMinutes * 60 * 1000).toISOString();

  await supabaseAdmin.from("password_reset_tokens").insert({
    auth_user_id: profile.auth_user_id,
    token,
    expires_at: expiresAt,
  });

  return {
    token,
    expiresAt,
    profile,
  };
}

export async function consumePasswordReset(token: string, password: string) {
  const { data: resetToken, error } = await supabaseAdmin
    .from("password_reset_tokens")
    .select("*")
    .eq("token", token)
    .is("used_at", null)
    .gt("expires_at", new Date().toISOString())
    .maybeSingle();

  if (error || !resetToken) {
    throw new AppError(400, "INVALID_RESET_TOKEN", "The reset token is invalid or expired.");
  }

  const result = await supabaseAdmin.auth.admin.updateUserById(String(resetToken.auth_user_id), {
    password,
  });

  if (result.error) {
    throw new AppError(400, "PASSWORD_RESET_FAILED", result.error.message);
  }

  await supabaseAdmin
    .from("password_reset_tokens")
    .update({ used_at: new Date().toISOString() })
    .eq("id", resetToken.id);
}

export function clearAppSession(reply: FastifyReply) {
  clearSessionCookies(reply);
}
