import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getOrganisationTemplateWhereInput } from '@documenso/lib/server-only/template/get-organisation-template-by-id';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';

import { maybeAuthenticatedProcedure } from '../trpc';
import {
  ZGetEnvelopeItemsByTokenRequestSchema,
  ZGetEnvelopeItemsByTokenResponseSchema,
} from './get-envelope-items-by-token.types';

// Not intended for V2 API usage.
// NOTE: THIS IS A PUBLIC PROCEDURE
export const getEnvelopeItemsByTokenRoute = maybeAuthenticatedProcedure
  .input(ZGetEnvelopeItemsByTokenRequestSchema)
  .output(ZGetEnvelopeItemsByTokenResponseSchema)
  .query(async ({ input, ctx }) => {
    const { teamId, user } = ctx;

    const { envelopeId, access } = input;

    ctx.logger.info({
      input: {
        envelopeId,
        access,
      },
    });

    if (access.type === 'user') {
      if (!user || !teamId) {
        throw new AppError(AppErrorCode.UNAUTHORIZED, {
          message: 'User not found',
        });
      }

      const { envelopeItems: data } = await handleGetEnvelopeItemsByUser({
        envelopeId,
        userId: user.id,
        teamId,
      });

      return {
        data,
      };
    }

    const { envelopeItems: data } = await handleGetEnvelopeItemsByToken({
      envelopeId,
      token: access.token,
    });

    return {
      data,
    };
  });

const handleGetEnvelopeItemsByToken = async ({ envelopeId, token }: { envelopeId: string; token: string }) => {
  const envelope = await prisma.envelope.findFirst({
    where: {
      id: envelopeId,
      type: EnvelopeType.DOCUMENT, // You cannot get template envelope items by token.
      recipients: {
        some: {
          token,
        },
      },
    },
    include: {
      envelopeItems: {
        include: {
          documentData: true,
        },
      },
    },
  });

  if (!envelope) {
    throw new AppError(AppErrorCode.NOT_FOUND, {
      message: 'Envelope could not be found',
    });
  }

  return {
    envelopeItems: envelope.envelopeItems,
  };
};

const handleGetEnvelopeItemsByUser = async ({
  envelopeId,
  userId,
  teamId,
}: {
  envelopeId: string;
  userId: number;
  teamId: number;
}) => {
  const { envelopeWhereInput, team: callerTeam } = await getEnvelopeWhereInput({
    id: {
      type: 'envelopeId',
      id: envelopeId,
    },
    type: null,
    userId,
    teamId,
  });

  // Try the standard team-scoped access path first (owner / current team / team email).
  let envelope = await prisma.envelope.findUnique({
    where: envelopeWhereInput,
    include: {
      envelopeItems: {
        include: {
          documentData: true,
        },
      },
    },
  });

  // Fallback: if the envelope is an ORGANISATION template owned by a sibling team
  // in the caller's organisation, allow read access to the items metadata.
  // Mirrors the access logic used by `createDocumentFromTemplate` and the
  // file-download endpoint's `checkEnvelopeFileAccess` so this route stays in
  // sync with where actual file access is granted.
  if (!envelope) {
    envelope = await prisma.envelope.findFirst({
      where: getOrganisationTemplateWhereInput({
        id: { type: 'envelopeId', id: envelopeId },
        organisationId: callerTeam.organisationId,
        teamRole: callerTeam.currentTeamRole,
      }),
      include: {
        envelopeItems: {
          include: {
            documentData: true,
          },
        },
      },
    });
  }

  if (!envelope) {
    throw new AppError(AppErrorCode.NOT_FOUND, {
      message: 'Envelope could not be found',
    });
  }

  return {
    envelopeItems: envelope.envelopeItems,
  };
};
