import type { FastifyReply, FastifyRequest } from "fastify";
import { cookieConfig } from "../config.js";
import { AppError } from "./errors.js";
import { createAuthClient, supabaseAdmin } from "./supabase.js";
import type { SessionPayload } from "../types.js";

const cookieBase = {
  httpOnly: true,
  sameSite: "lax" as const,
  path: "/"
};

export function clearSessionCookies(reply: FastifyReply) {
  reply.clearCookie(cookieConfig.accessTokenName, cookieBase);
  reply.clearCookie(cookieConfig.refreshTokenName, cookieBase);
  reply.clearCookie(cookieConfig.activeOrganizationName, {
    ...cookieBase,
    httpOnly: false,
  });
}

export function setSessionCookies(
  reply: FastifyReply,
  session: { access_token: string; refresh_token: string; expires_in?: number },
  organizationId?: string | null
) {
  reply.setCookie(cookieConfig.accessTokenName, session.access_token, {
    ...cookieBase,
    maxAge: session.expires_in ?? 60 * 60
  });
  reply.setCookie(cookieConfig.refreshTokenName, session.refresh_token, {
    ...cookieBase,
    maxAge: 60 * 60 * 24 * 30
  });

  if (organizationId) {
    reply.setCookie(cookieConfig.activeOrganizationName, organizationId, {
      ...cookieBase,
      httpOnly: false,
      maxAge: 60 * 60 * 24 * 30
    });
  }
}

function syncActiveOrganizationCookie(reply: FastifyReply, organizationId?: string | null) {
  if (organizationId) {
    reply.setCookie(cookieConfig.activeOrganizationName, organizationId, {
      ...cookieBase,
      httpOnly: false,
      maxAge: 60 * 60 * 24 * 30
    });
    return;
  }

  reply.clearCookie(cookieConfig.activeOrganizationName, {
    ...cookieBase,
    httpOnly: false,
  });
}

export async function hydrateAuth(request: FastifyRequest, reply: FastifyReply) {
  const accessToken = request.cookies[cookieConfig.accessTokenName] ?? null;
  const refreshToken = request.cookies[cookieConfig.refreshTokenName] ?? null;

  request.authContext = {
    accessToken,
    refreshToken,
    user: null,
    profile: null,
    organizationId: request.cookies[cookieConfig.activeOrganizationName] ?? null
  };

  if (!accessToken && !refreshToken) {
    return;
  }

  const authClient = createAuthClient();

  if (accessToken) {
    const { data } = await authClient.auth.getUser(accessToken);

    if (data.user) {
      request.authContext.user = data.user;
      return;
    }
  }

  if (refreshToken) {
    const { data } = await authClient.auth.refreshSession({ refresh_token: refreshToken });

    if (data.session?.user) {
      request.authContext.accessToken = data.session.access_token;
      request.authContext.refreshToken = data.session.refresh_token;
      request.authContext.user = data.session.user;
      setSessionCookies(reply, data.session, request.authContext.organizationId);
    }
  }
}

export async function attachProfile(request: FastifyRequest, reply: FastifyReply) {
  if (!request.authContext.user) {
    return;
  }

  const { data: profile } = await supabaseAdmin
    .from("user_profiles")
    .select("id, auth_user_id, email, full_name, avatar_path, role, default_organization_id")
    .eq("auth_user_id", request.authContext.user.id)
    .maybeSingle();

  request.authContext.profile = profile ?? null;

  const userId = request.authContext.user.id;
  const requestedOrganizationId = request.authContext.organizationId;

  const { data: memberships } = await supabaseAdmin
    .from("organization_members")
    .select("organization_id")
    .eq("auth_user_id", userId)
    .order("created_at", { ascending: true });

  const membershipOrganizationIds = new Set(
    (memberships ?? [])
      .map((membership) => String(membership.organization_id ?? ""))
      .filter(Boolean)
  );

  const defaultOrganizationId =
    typeof profile?.default_organization_id === "string" ? profile.default_organization_id : null;

  const resolvedOrganizationId =
    (requestedOrganizationId && membershipOrganizationIds.has(requestedOrganizationId) ? requestedOrganizationId : null)
    ?? (defaultOrganizationId && membershipOrganizationIds.has(defaultOrganizationId) ? defaultOrganizationId : null)
    ?? Array.from(membershipOrganizationIds)[0]
    ?? null;

  request.authContext.organizationId = resolvedOrganizationId;

  if (requestedOrganizationId !== resolvedOrganizationId) {
    syncActiveOrganizationCookie(reply, resolvedOrganizationId);
  }
}

export const clearSessionCookie = clearSessionCookies;
export const setSessionCookie = setSessionCookies;

export function requireSession(request: FastifyRequest): SessionPayload {
  const user = request.authContext.user;
  const profile = request.authContext.profile;

  if (!user || !profile) {
    throw new AppError(401, "UNAUTHORIZED", "You must be signed in to access this resource.");
  }

  return {
    accessToken: request.authContext.accessToken,
    refreshToken: request.authContext.refreshToken,
    activeOrganizationId: request.authContext.organizationId,
    defaultOrganizationId:
      typeof profile.default_organization_id === "string" ? profile.default_organization_id : null,
    profileRole: String(profile.role ?? "user"),
    userId: user.id,
    email: user.email ?? "",
  };
}

export function requirePlatformAdmin(request: FastifyRequest) {
  const session = requireSession(request);

  if (session.profileRole !== "platform_admin") {
    throw new AppError(403, "FORBIDDEN", "Platform admin access is required.");
  }

  return session;
}
