import { NextRequest } from "next/server";

import { auth } from "@/config/auth";
import { AppError } from "@/core/errors/app-error";
import { ResponseHelper } from "@/shared/infra/http/response-helper";

export interface AuthenticatedUser {
  id: string;
}

export interface IdRouteParam {
  id: string;
}

interface RouteHandlerArgs<TParams> {
  request: NextRequest;
  user: AuthenticatedUser;
  params: TParams;
}

interface RouteHandlerArgsWithoutParams {
  request: NextRequest;
  user: AuthenticatedUser;
}

type RouteContext<TParams> = { params: Promise<TParams> };

async function resolveAuthenticatedUser(): Promise<AuthenticatedUser> {
  const session = await auth();
  if (!session?.user?.id) {
    throw AppError.unauthorized();
  }
  return { id: session.user.id };
}

function handleRouteError(error: unknown): Response {
  if (error instanceof AppError) {
    return ResponseHelper.error(error.message, error.statusCode);
  }
  console.error(error);
  return ResponseHelper.error("Erro interno do servidor", 500);
}

// Overload for static routes (no dynamic segments) — Next.js calls these with a single argument.
export function createAuthenticatedRouteHandler(
  handler: (args: RouteHandlerArgsWithoutParams) => Promise<Response>
): (request: NextRequest) => Promise<Response>;
// Overload for dynamic routes (e.g. `[id]`) — Next.js always supplies a required context argument.
export function createAuthenticatedRouteHandler<TParams>(
  handler: (args: RouteHandlerArgs<TParams>) => Promise<Response>
): (request: NextRequest, context: RouteContext<TParams>) => Promise<Response>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createAuthenticatedRouteHandler(handler: (args: any) => Promise<Response>) {
  return async (request: NextRequest, context?: RouteContext<unknown>): Promise<Response> => {
    try {
      const user = await resolveAuthenticatedUser();

      if (context) {
        const params = await context.params;
        return await handler({ request, user, params });
      }

      return await handler({ request, user });
    } catch (error) {
      return handleRouteError(error);
    }
  };
}
