import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { generateDatabaseId } from '@documenso/lib/universal/id';
import { getHighestOrganisationRoleInGroup } from '@documenso/lib/utils/organisations';
import { prisma } from '@documenso/prisma';
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';

import { adminProcedure } from '../trpc';
import {
  ZPromoteMemberToOwnerRequestSchema,
  ZPromoteMemberToOwnerResponseSchema,
} from './promote-member-to-owner.types';

export const promoteMemberToOwnerRoute = adminProcedure
  .input(ZPromoteMemberToOwnerRequestSchema)
  .output(ZPromoteMemberToOwnerResponseSchema)
  .mutation(async ({ input, ctx }) => {
    const { organisationId, userId } = input;

    ctx.logger.info({
      input: {
        organisationId,
        userId,
      },
    });

    // First, verify the organisation exists and get member details with groups
    const organisation = await prisma.organisation.findUnique({
      where: {
        id: organisationId,
      },
      include: {
        groups: {
          where: {
            type: OrganisationGroupType.INTERNAL_ORGANISATION,
          },
        },
        members: {
          where: {
            userId,
          },
          include: {
            organisationGroupMembers: {
              include: {
                group: true,
              },
            },
          },
        },
      },
    });

    if (!organisation) {
      throw new AppError(AppErrorCode.NOT_FOUND, {
        message: 'Organisation not found',
      });
    }

    // Verify the user is a member of the organisation
    const [member] = organisation.members;

    if (!member) {
      throw new AppError(AppErrorCode.NOT_FOUND, {
        message: 'User is not a member of this organisation',
      });
    }

    // Verify the user is not already the owner
    if (organisation.ownerUserId === userId) {
      throw new AppError(AppErrorCode.INVALID_REQUEST, {
        message: 'User is already the owner of this organisation',
      });
    }

    // Get current organisation role
    const currentOrganisationRole = getHighestOrganisationRoleInGroup(
      member.organisationGroupMembers.flatMap((member) => member.group),
    );

    // Find the current and target organisation groups
    const currentMemberGroup = organisation.groups.find((group) => group.organisationRole === currentOrganisationRole);

    const adminGroup = organisation.groups.find((group) => group.organisationRole === OrganisationMemberRole.ADMIN);

    if (!currentMemberGroup) {
      throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
        message: 'Current member group not found',
      });
    }

    if (!adminGroup) {
      throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
        message: 'Admin group not found',
      });
    }

    // Update the organisation owner and member role in a transaction
    await prisma.$transaction(async (tx) => {
      // Update the organisation to set the new owner
      await tx.organisation.update({
        where: {
          id: organisationId,
        },
        data: {
          ownerUserId: userId,
        },
      });

      // Only update role if the user is not already an admin then add them to the admin group
      if (currentOrganisationRole !== OrganisationMemberRole.ADMIN) {
        await tx.organisationGroupMember.create({
          data: {
            id: generateDatabaseId('group_member'),
            organisationMemberId: member.id,
            groupId: adminGroup.id,
          },
        });
      }
    });
  });
