import { prisma } from '@documenso/prisma';

import { ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/organisations';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams';
import { AppError } from '../../errors/app-error';
import type { ApiRequestMetadata } from '../../universal/extract-request-metadata';
import { optimiseAvatar } from '../../utils/images/avatar';
import { buildOrganisationWhereQuery } from '../../utils/organisations';
import { buildTeamWhereQuery } from '../../utils/teams';

export type SetAvatarImageOptions = {
  userId: number;
  target:
    | {
        type: 'user';
      }
    | {
        type: 'team';
        teamId: number;
      }
    | {
        type: 'organisation';
        organisationId: string;
      };
  bytes?: string | null;
  requestMetadata: ApiRequestMetadata;
};

/**
 * Pretty nasty but will do for now.
 */
export const setAvatarImage = async ({ userId, target, bytes, requestMetadata }: SetAvatarImageOptions) => {
  let oldAvatarImageId: string | null = null;

  if (target.type === 'team') {
    const team = await prisma.team.findFirst({
      where: buildTeamWhereQuery({
        teamId: target.teamId,
        userId,
        roles: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_TEAM'],
      }),
    });

    if (!team) {
      throw new AppError('TEAM_NOT_FOUND', {
        statusCode: 404,
      });
    }

    oldAvatarImageId = team.avatarImageId;
  } else if (target.type === 'organisation') {
    const organisation = await prisma.organisation.findFirst({
      where: buildOrganisationWhereQuery({
        organisationId: target.organisationId,
        userId,
        roles: ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_ORGANISATION'],
      }),
    });

    if (!organisation) {
      throw new AppError('ORGANISATION_NOT_FOUND', {
        statusCode: 404,
      });
    }

    oldAvatarImageId = organisation.avatarImageId;
  } else {
    const user = await prisma.user.findUnique({
      where: {
        id: userId,
      },
      include: {
        avatarImage: true,
      },
    });

    if (!user) {
      throw new AppError('USER_NOT_FOUND', {
        statusCode: 404,
      });
    }

    oldAvatarImageId = user.avatarImageId;
  }

  if (oldAvatarImageId) {
    await prisma.avatarImage.delete({
      where: {
        id: oldAvatarImageId,
      },
    });
  }

  let newAvatarImageId: string | null = null;

  if (bytes) {
    const optimisedBytes = await optimiseAvatar(bytes);

    const avatarImage = await prisma.avatarImage.create({
      data: {
        bytes: optimisedBytes.toString('base64'),
      },
    });

    newAvatarImageId = avatarImage.id;
  }

  // TODO: Audit Logs

  if (target.type === 'team') {
    await prisma.team.update({
      where: {
        id: target.teamId,
      },
      data: {
        avatarImageId: newAvatarImageId,
      },
    });
  } else if (target.type === 'organisation') {
    await prisma.organisation.update({
      where: {
        id: target.organisationId,
      },
      data: {
        avatarImageId: newAvatarImageId,
      },
    });
  } else {
    await prisma.user.update({
      where: {
        id: userId,
      },
      data: {
        avatarImageId: newAvatarImageId,
      },
    });
  }

  return newAvatarImageId;
};
