import { S3Client } from "@aws-sdk/client-s3";
import { fromIni, fromTemporaryCredentials } from "@aws-sdk/credential-providers";
import { createPresignedPost } from "@aws-sdk/s3-presigned-post";
import { NextRequest, NextResponse } from "next/server";

export const runtime = "nodejs";

const ALLOWED_IMAGE_TYPES = new Set(["image/jpeg", "image/png", "image/webp"]);
const UPLOAD_FOLDERS = {
  avatar: "profile_pics",
  profile: "profile_pics",
  olympiadProfile: "profile_pics",
  generatedImage: "generated_images",
} as const;

type UploadPurpose = keyof typeof UPLOAD_FOLDERS;

const getEnv = (name: string) => {
  const value = process.env[name];
  if (!value) {
    throw new Error(`${name} is not configured`);
  }
  return value;
};

const getMaxUploadBytes = () => {
  const configured = Number(process.env.S3_MAX_IMAGE_UPLOAD_BYTES);
  return Number.isFinite(configured) && configured > 0
    ? configured
    : 2.5 * 1024 * 1024;
};

const getS3Client = () => {
  const region = getEnv("AWS_REGION");
  const roleArn = process.env.AWS_UPLOAD_ROLE_ARN;
  const profile = process.env.AWS_PROFILE;

  return new S3Client({
    region,
    credentials: roleArn
      ? fromTemporaryCredentials({
          masterCredentials: profile ? fromIni({ profile }) : undefined,
          clientConfig: { region },
          params: {
            RoleArn: roleArn,
            RoleSessionName: "give-geeta-upload-signer",
          },
        })
      : undefined,
  });
};

const isCredentialsError = (error: unknown) => {
  return (
    error instanceof Error &&
    (error.name === "CredentialsProviderError" ||
      error.message.includes("Could not load credentials"))
  );
};

const getFileExtension = (contentType: string) => {
  switch (contentType) {
    case "image/png":
      return "png";
    case "image/webp":
      return "webp";
    default:
      return "jpg";
  }
};

export async function POST(request: NextRequest) {
  try {
    const authorization = request.headers.get("authorization");
    if (!authorization?.startsWith("Bearer ")) {
      return NextResponse.json({ message: "Unauthorized." }, { status: 401 });
    }

    const body = await request.json();
    const contentType = String(body?.contentType || "");
    const fileSize = Number(body?.fileSize || 0);
    const purpose = String(body?.purpose || "profile") as UploadPurpose;
    const maxUploadBytes = getMaxUploadBytes();

    if (!ALLOWED_IMAGE_TYPES.has(contentType)) {
      return NextResponse.json(
        { message: "Only JPG, PNG and WEBP images are allowed." },
        { status: 400 },
      );
    }

    if (!Number.isFinite(fileSize) || fileSize <= 0 || fileSize > maxUploadBytes) {
      return NextResponse.json(
        { message: "Image size is not allowed." },
        { status: 400 },
      );
    }

    if (!(purpose in UPLOAD_FOLDERS)) {
      return NextResponse.json(
        { message: "Invalid upload purpose." },
        { status: 400 },
      );
    }

    const bucket = getEnv("S3_UPLOAD_BUCKET");
    const cloudFrontDomain = getEnv("CLOUDFRONT_DOMAIN").replace(/^https?:\/\//, "");
    const folder = UPLOAD_FOLDERS[purpose];
    const key = `${folder}/${crypto.randomUUID()}.${getFileExtension(contentType)}`;

    const presignedPost = await createPresignedPost(getS3Client(), {
      Bucket: bucket,
      Key: key,
      Conditions: [
        ["content-length-range", 1, maxUploadBytes],
        ["eq", "$Content-Type", contentType],
        ["eq", "$key", key],
      ],
      Fields: {
        "Content-Type": contentType,
      },
      Expires: 60,
    });

    return NextResponse.json({
      ...presignedPost,
      key,
      publicUrl: `https://${cloudFrontDomain}/${key}`,
    });
  } catch (error) {
    console.error("Failed to create S3 presigned post", error);

    if (isCredentialsError(error)) {
      return NextResponse.json(
        {
          message:
            "AWS credentials are not available to assume AWS_UPLOAD_ROLE_ARN. In production, attach a trusted AWS execution role to the deployment. For local development, configure AWS SSO/profile and set AWS_PROFILE, or run without AWS_UPLOAD_ROLE_ARN only when the default provider already has S3 access.",
        },
        { status: 500 },
      );
    }

    return NextResponse.json(
      { message: "Unable to prepare upload." },
      { status: 500 },
    );
  }
}
