import { ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/organisations';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { normalizeBrandingColors } from '@documenso/lib/utils/normalize-branding-colors';
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
import { type SanitizeBrandingCssWarning, sanitizeBrandingCss } from '@documenso/lib/utils/sanitize-branding-css';
import { buildTeamWhereQuery } from '@documenso/lib/utils/teams';
import { prisma } from '@documenso/prisma';
import { OrganisationType, Prisma } from '@prisma/client';

import { authenticatedProcedure } from '../trpc';
import { ZUpdateTeamSettingsRequestSchema, ZUpdateTeamSettingsResponseSchema } from './update-team-settings.types';

export const updateTeamSettingsRoute = authenticatedProcedure
  .input(ZUpdateTeamSettingsRequestSchema)
  .output(ZUpdateTeamSettingsResponseSchema)
  .mutation(async ({ ctx, input }) => {
    const { user } = ctx;
    const { teamId, data } = input;

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

    const {
      // Document related settings.
      documentVisibility,
      documentLanguage,
      documentTimezone,
      documentDateFormat,
      includeSenderDetails,
      includeSigningCertificate,
      includeAuditLog,
      typedSignatureEnabled,
      uploadSignatureEnabled,
      drawSignatureEnabled,
      delegateDocumentOwnership,
      envelopeExpirationPeriod,
      reminderSettings,

      // Branding related settings.
      brandingEnabled,
      brandingUrl,
      brandingCompanyDetails,
      brandingColors,
      brandingCss,

      // Email related settings.
      emailId,
      emailReplyTo,
      // emailReplyToName,
      emailDocumentSettings,

      // Default recipients settings.
      defaultRecipients,
      // AI features settings.
      aiFeaturesEnabled,
    } = data;

    if (Object.values(data).length === 0) {
      throw new AppError(AppErrorCode.INVALID_BODY, {
        message: 'No settings to update',
      });
    }

    // Signatures will only be inherited if all are NULL.
    if (typedSignatureEnabled === false && uploadSignatureEnabled === false && drawSignatureEnabled === false) {
      throw new AppError(AppErrorCode.INVALID_BODY, {
        message: 'At least one signature type must be enabled',
      });
    }

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

    if (!team) {
      throw new AppError(AppErrorCode.UNAUTHORIZED, {
        message: 'You do not have permission to update this team.',
      });
    }

    // Validate that the email ID belongs to the organisation.
    if (emailId) {
      const email = await prisma.organisationEmail.findFirst({
        where: {
          id: emailId,
          organisationId: team.organisationId,
        },
      });

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

    const organisation = await prisma.organisation.findFirst({
      where: buildOrganisationWhereQuery({
        organisationId: team.organisationId,
        userId: user.id,
        roles: ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_ORGANISATION'],
      }),
      select: {
        type: true,
        organisationGlobalSettings: {
          select: {
            includeSenderDetails: true,
          },
        },
      },
    });

    const isPersonalOrganisation = organisation?.type === OrganisationType.PERSONAL;
    const currentIncludeSenderDetails = organisation?.organisationGlobalSettings.includeSenderDetails;

    const isChangingIncludeSenderDetails =
      includeSenderDetails !== undefined && includeSenderDetails !== currentIncludeSenderDetails;

    // Personal teams cannot change the sender details — drop the field (no-op)
    // instead of rejecting the whole update.
    const derivedIncludeSenderDetails =
      isPersonalOrganisation && isChangingIncludeSenderDetails ? undefined : includeSenderDetails;

    // Sanitize custom branding CSS at write time. `null` means inherit-from-org
    // for teams, so only run the sanitiser when an explicit string is provided.
    // An empty string after sanitisation is collapsed to `null` so the team
    // row inherits rather than persisting an empty override.
    let cssWarnings: SanitizeBrandingCssWarning[] | undefined;
    let sanitizedBrandingCss: string | null | undefined;

    if (brandingCss === null) {
      sanitizedBrandingCss = null;
    } else if (typeof brandingCss === 'string') {
      const result = sanitizeBrandingCss(brandingCss);
      sanitizedBrandingCss = result.css.trim() === '' ? null : result.css;
      cssWarnings = result.warnings;
    }

    // Strip empty-string colour values; collapse to `null` when the payload
    // contains no overrides. For teams this matters because brandingEnabled
    // = null inherits from the org — leaving `{}` here would persist a real
    // override of nothing once a team toggles brandingEnabled = true.
    const normalizedBrandingColors = normalizeBrandingColors(brandingColors);

    await prisma.team.update({
      where: {
        id: teamId,
      },
      data: {
        teamGlobalSettings: {
          update: {
            // Document related settings.
            documentVisibility,
            documentLanguage,
            documentTimezone,
            documentDateFormat,
            includeSenderDetails: derivedIncludeSenderDetails,
            includeSigningCertificate,
            includeAuditLog,
            typedSignatureEnabled,
            uploadSignatureEnabled,
            drawSignatureEnabled,
            delegateDocumentOwnership,
            envelopeExpirationPeriod: envelopeExpirationPeriod === null ? Prisma.DbNull : envelopeExpirationPeriod,
            reminderSettings: reminderSettings === null ? Prisma.DbNull : reminderSettings,

            // Branding related settings.
            brandingEnabled,
            brandingUrl,
            brandingCompanyDetails,
            brandingColors: normalizedBrandingColors === null ? Prisma.DbNull : normalizedBrandingColors,
            brandingCss: sanitizedBrandingCss,

            // Email related settings.
            emailId,
            emailReplyTo,
            // emailReplyToName,
            emailDocumentSettings: emailDocumentSettings === null ? Prisma.DbNull : emailDocumentSettings,
            defaultRecipients: defaultRecipients === null ? Prisma.DbNull : defaultRecipients,

            // AI features settings.
            aiFeaturesEnabled,
          },
        },
      },
    });

    return {
      cssWarnings: cssWarnings && cssWarnings.length > 0 ? cssWarnings : undefined,
    };
  });
