import express, { Request, Response } from "express";
import { JwtPayload } from "jsonwebtoken";
import { PrismaClient } from "../../prisma/app/generated/prisma/client";
import dotenv from "dotenv";
import { StatusCodes } from "http-status-codes";
import multer from "multer";
import { minioClient } from "../server";

const app = express();
app.use(express.json());
const prisma = new PrismaClient();
// load environment variables from .env file
dotenv.config();

type PublicUser = {
  id: string;
  username: string;
  bio: string | null;
  profilePictureUrl: string | null;
  followers: number;
  following: number;
  posts: number;
};
const getUser: (userId: string) => Promise<PublicUser | undefined> = async (
  userId: string
) => {
  const user = await prisma.user.findUnique({
    where: { id: userId },
    include: { profilePicture: true },
  });
  if (user) {
    let profilePictureUrl: string | null = null;
    if (user.profilePicture) {
      profilePictureUrl = await minioClient.presignedGetObject(
        user.profilePicture.bucket,
        user.profilePicture.objectName,
        60 * 10
      );
    }
    const followerCount = await prisma.follow.count({
      where: {
        followedUserId: userId,
      },
    });

    const followingCount = await prisma.follow.count({
      where: {
        followingUserId: userId,
      },
    });
    const postCount = await prisma.post.count({
      where: {
        userId: userId,
      },
    });
    const publicUser: PublicUser = {
      id: user.id,
      username: user.username,
      bio: user.bio,
      profilePictureUrl,
      followers: followerCount,
      following: followingCount,
      posts: postCount,
    };
    return publicUser;
  }
  return undefined;
};

export const uploadProfilePicture = async (req: Request, res: Response) => {
  const user: JwtPayload = req.user!;
  const file = req.file as Express.Multer.File;
  const BUCKET = "images"; // Name of the bucket where the images are stored
  try {
    const objectName = `${user.sub}/profile.${file.mimetype.split("/")[1]}`;
    await minioClient.putObject(BUCKET, objectName, file.buffer);
    const url = await minioClient.presignedGetObject(
      BUCKET,
      objectName,
      60 * 10
    );
    const oldImage = await prisma.user.findUnique({
      where: { id: user.sub },
      select: { profilePictureId: true },
    });

    const media = await prisma.media.create({
      data: {
        originalFilename: file.originalname,
        objectName: objectName,
        mimeType: file.mimetype,
        size: file.size,
        bucket: BUCKET,
        uploader: { connect: { id: user.sub } },
      },
    });

    await prisma.user.update({
      where: { id: user.sub },
      data: {
        profilePicture: {
          connect: { id: media.id },
        },
      },
    });

    if (oldImage?.profilePictureId && oldImage.profilePictureId !== media.id) {
      await prisma.media.delete({
        where: { id: oldImage.profilePictureId },
      });
    }

    res.status(StatusCodes.CREATED).json({
      originalName: file.originalname,
      objectName: objectName,
      size: file.size,
      mimetype: file.mimetype,
      url: url,
    });
  } catch (err) {
    console.error(err);
    res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
      error: "Upload failed",
      details: [{ message: "Internal server error" }],
    });
  }
};

export const getProfilePicture = async (req: Request, res: Response) => {
  const username: string = req.params.username;
  if (!username) {
    res.status(StatusCodes.BAD_REQUEST).json({
      error: "no username",
      details: [{ message: "Username is required" }],
    });
    return;
  }
  try {
    const user = await prisma.user.findUnique({
      where: { username },
      include: { profilePicture: true },
    });

    if (!user) {
      res.status(StatusCodes.NOT_FOUND).json({
        error: "User not found",
        details: [
          { message: `User with username '${username}' does not exist.` },
        ],
      });
      return;
    }

    if (!user.profilePicture) {
      res.status(StatusCodes.NOT_FOUND).json({
        error: "No profile picture",
        details: [{ message: "This user does not have a profile picture." }],
      });
      return;
    }

    const profilePictureUrl = await minioClient.presignedGetObject(
      user.profilePicture.bucket,
      user.profilePicture.objectName,
      60 * 10 // 10 minutes expiration
    );

    res.status(StatusCodes.OK).json({ url: profilePictureUrl });
  } catch (err) {
    console.error(err);
    res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
      error: "Retrieving image failed",
      details: ["Internal server error"],
    });
    return;
  }
};

export const updateBio = async (req: Request, res: Response) => {
  const user: JwtPayload = req.user!;
  const bio: string = req.body.bio;
  try {
    const updatedUser = await prisma.user.update({
      where: { id: user.sub },
      data: {
        bio: bio,
      },
    });
    const publicUser: PublicUser | undefined = await getUser(user.sub!);
    res.status(StatusCodes.OK).json({
      message: "User found",
      data: publicUser,
    });
  } catch (err) {
    console.error(err);
    res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
      error: "Updating bio failed",
      details: [{ message: "Internal Server error" }],
    });
  }
};
// Endpoint to get user data
export const getProfile = async (req: Request, res: Response) => {
  const username: string = req.params.username as string;
  const requestingUser: JwtPayload | undefined = req.user;
  if (!username) {
    res.status(StatusCodes.BAD_REQUEST).json({
      error: "no username",
      details: [{ message: "Username is required" }],
    });
    return;
  }
  try {
    const user = await prisma.user.findUnique({
      where: { username },
      include: { profilePicture: true },
    });

    if (!user) {
      res.status(StatusCodes.NOT_FOUND).json({
        error: "User not found",
        details: [
          { message: `User with username '${username}' does not exist.` },
        ],
      });
      return;
    }

    const publicUser: PublicUser | undefined = await getUser(user.id);

    let isFollowing : boolean = false; 
    if(requestingUser) {
      const followingData = await prisma.follow.findFirst({
        where: {
          followedUserId: user.id,
          followingUserId: requestingUser.id,
        }
      })
      if(followingData) {
        isFollowing = true; 
      } 
    }

    res.status(StatusCodes.OK).json({
      message: "User found",
      data: {...publicUser, isFollowing},
    });
  } catch (err) {
    console.error(err);
    res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
      error: "Retrieving image failed",
      details: ["Error while retrieving the image"],
    });
    return;
  }
};
