import { Outlet, useLoaderData } from "@remix-run/react";
import { redirect, type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { RouteErrorDisplay } from "~/components/ErrorDisplay";
import { DashboardAgent } from "~/components/dashboard-agent/DashboardAgent";
import { prisma } from "~/db.server";
import { env } from "~/env.server";
import { canAccessDashboardAgent } from "~/v3/canAccessDashboardAgent.server";
import { redirectWithErrorMessage } from "~/models/message.server";
import { updateCurrentProjectEnvironmentId } from "~/services/dashboardPreferences.server";
import { logger } from "~/services/logger.server";
import { requireUser } from "~/services/session.server";
import { tenantContext } from "~/services/tenantContext.server";
import { EnvironmentParamSchema, v3ProjectPath } from "~/utils/pathBuilder";

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
  const user = await requireUser(request);
  const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);

  const project = await prisma.project.findFirst({
    where: {
      slug: projectParam,
      organization: {
        slug: organizationSlug,
        members: {
          some: {
            userId: user.id,
          },
        },
      },
      deletedAt: null,
    },
    select: {
      id: true,
      externalRef: true,
      organization: { select: { id: true, featureFlags: true } },
      environments: {
        select: {
          id: true,
          type: true,
          slug: true,
          orgMember: {
            select: {
              userId: true,
            },
          },
        },
      },
    },
  });

  if (!project) {
    logger.error("Project not found", { params, user });
    throw new Response("Project not Found", { status: 404, statusText: "Project not found" });
  }

  const environments = project.environments.filter((env) => env.slug === envParam);
  if (environments.length === 0) {
    return redirect(v3ProjectPath({ slug: organizationSlug }, { slug: projectParam }));
  }

  let environmentId: string | undefined = undefined;
  let environmentType: "DEVELOPMENT" | "PREVIEW" | "STAGING" | "PRODUCTION" | undefined;

  if (environments.length > 1) {
    const bestEnvironment = environments.find((env) => env.orgMember?.userId === user.id);
    if (!bestEnvironment) {
      throw new Response("Environment not Found", {
        status: 404,
        statusText: "Environment not found",
      });
    }

    environmentId = bestEnvironment.id;
    environmentType = bestEnvironment.type;
  } else {
    environmentId = environments[0].id;
    environmentType = environments[0].type;
  }

  // userId is enriched higher up in `_app/route.tsx`; only stamp tenant fields here.
  tenantContext.enrich({
    orgId: project.organization.id,
    projectId: project.id,
    projectRef: project.externalRef,
    envId: environmentId,
    envType: environmentType,
  });

  await updateCurrentProjectEnvironmentId({ user: user, projectId: project.id, environmentId });

  // Resolve dashboard-agent access here (single source of truth: global env,
  // admins/impersonators, then the global/per-org feature flag, default off) so
  // the launcher button is hidden when it's not enabled. The org's featureFlags
  // came from the membership-checked project query above, so we pass them in to
  // avoid a second org lookup.
  const hasDashboardAgentAccess = await canAccessDashboardAgent({
    userId: user.id,
    isAdmin: user.admin,
    isImpersonating: user.isImpersonating,
    organizationSlug,
    orgFeatureFlags: (project.organization.featureFlags as Record<string, unknown>) ?? {},
  });

  return {
    ...project,
    hasDashboardAgentAccess,
  };
};

export default function Page() {
  const { hasDashboardAgentAccess } = useLoaderData<typeof loader>();
  return (
    <DashboardAgent hasAccess={hasDashboardAgentAccess}>
      <Outlet />
    </DashboardAgent>
  );
}

// Caught here (inside the project SideMenu's Outlet) rather than at the project
// layout, so a permission denial or error on any env-scoped page renders in the
// content pane with the SideMenu intact. RouteErrorDisplay renders the
// permission panel for a 403 and the generic error otherwise.
export function ErrorBoundary() {
  return <RouteErrorDisplay />;
}
