import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import type { TRecipientLite } from '@documenso/lib/types/recipient';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { RecipientRole, SendStatus, SigningStatus } from '@prisma/client';
import { Check, ChevronsUpDown, Info } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { sortBy } from 'remeda';

import { getRecipientColorStyles } from '../lib/recipient-colors';
import { cn } from '../lib/utils';
import { Button } from './button';
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from './command';
import { Popover, PopoverContent, PopoverTrigger } from './popover';
import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip';

export interface RecipientSelectorProps {
  className?: string;
  selectedRecipient: TRecipientLite | null;
  onSelectedRecipientChange: (recipient: TRecipientLite) => void;
  recipients: TRecipientLite[];
  align?: 'center' | 'end' | 'start';
}

export const RecipientSelector = ({
  className,
  selectedRecipient,
  onSelectedRecipientChange,
  recipients,
  align = 'start',
}: RecipientSelectorProps) => {
  const { _ } = useLingui();
  const [showRecipientsSelector, setShowRecipientsSelector] = useState(false);

  const recipientsByRole = useMemo(() => {
    const recipientsWithRole: Record<RecipientRole, TRecipientLite[]> = {
      CC: [],
      VIEWER: [],
      SIGNER: [],
      APPROVER: [],
      ASSISTANT: [],
    };

    recipients.forEach((recipient) => {
      recipientsWithRole[recipient.role].push(recipient);
    });

    return recipientsWithRole;
  }, [recipients]);

  const recipientsByRoleToDisplay = useMemo(() => {
    return Object.entries(recipientsByRole)
      .filter(
        ([role]) => role !== RecipientRole.CC && role !== RecipientRole.VIEWER && role !== RecipientRole.ASSISTANT,
      )
      .map(
        ([role, roleRecipients]) =>
          [
            role,
            sortBy(roleRecipients, [(r) => r.signingOrder || Number.MAX_SAFE_INTEGER, 'asc'], [(r) => r.id, 'asc']),
          ] as [RecipientRole, TRecipientLite[]],
      );
  }, [recipientsByRole]);

  const getRecipientLabel = useCallback(
    (recipient: TRecipientLite) => {
      if (recipient.name && recipient.email) {
        return `${recipient.name} (${recipient.email})`;
      }

      if (recipient.name) {
        return recipient.name;
      }

      if (recipient.email) {
        return recipient.email;
      }

      // Since objects are basically pointers we can use `indexOf` rather than `findIndex`
      const index = recipients.indexOf(recipient);

      return `Recipient ${index + 1}`;
    },
    [recipients, selectedRecipient],
  );

  return (
    <Popover open={showRecipientsSelector} onOpenChange={setShowRecipientsSelector}>
      <PopoverTrigger asChild>
        <Button
          type="button"
          variant="outline"
          role="combobox"
          className={cn(
            'justify-between bg-background font-normal text-muted-foreground hover:text-foreground',
            getRecipientColorStyles(recipients.findIndex((r) => r.id === selectedRecipient?.id)).comboBoxTrigger,
            className,
          )}
        >
          {selectedRecipient && (
            <span className="flex-1 truncate text-left">{getRecipientLabel(selectedRecipient)}</span>
          )}

          <ChevronsUpDown className="ml-2 h-4 w-4" />
        </Button>
      </PopoverTrigger>

      <PopoverContent className="p-0" align={align}>
        <Command value={selectedRecipient ? selectedRecipient.id.toString() : undefined}>
          <CommandInput />

          <CommandEmpty>
            <span className="inline-block px-4 text-muted-foreground">
              <Trans>No recipient matching this description was found.</Trans>
            </span>
          </CommandEmpty>

          {recipientsByRoleToDisplay.map(([role, roleRecipients], roleIndex) => (
            <CommandGroup key={roleIndex}>
              <div className="mt-2 mb-1 ml-2 font-medium text-muted-foreground text-xs">
                {_(RECIPIENT_ROLES_DESCRIPTION[role].roleNamePlural)}
              </div>

              {roleRecipients.length === 0 && (
                <div key={`${role}-empty`} className="px-4 pt-2.5 pb-4 text-center text-muted-foreground/80 text-xs">
                  <Trans>No recipients with this role</Trans>
                </div>
              )}

              {roleRecipients.map((recipient) => (
                <CommandItem
                  key={recipient.id}
                  className={cn(
                    'px-2 last:mb-1 [&:not(:first-child)]:mt-1',
                    getRecipientColorStyles(recipients.findIndex((r) => r.id === recipient.id)).comboBoxItem,
                    {
                      'text-muted-foreground': recipient.sendStatus === SendStatus.SENT,
                    },
                  )}
                  onSelect={() => {
                    onSelectedRecipientChange(recipient);
                    setShowRecipientsSelector(false);
                  }}
                  disabled={recipient.signingStatus !== SigningStatus.NOT_SIGNED}
                >
                  <span
                    className={cn('truncate text-foreground/70', {
                      'text-foreground/80': recipient.id === selectedRecipient?.id,
                    })}
                  >
                    {getRecipientLabel(recipient)}
                  </span>

                  <div className="ml-auto flex items-center justify-center">
                    {recipient.sendStatus !== SendStatus.SENT ? (
                      <Check
                        aria-hidden={recipient.id !== selectedRecipient?.id}
                        className={cn('h-4 w-4 flex-shrink-0', {
                          'opacity-0': recipient.id !== selectedRecipient?.id,
                          'opacity-100': recipient.id === selectedRecipient?.id,
                        })}
                      />
                    ) : (
                      <Tooltip>
                        <TooltipTrigger>
                          <Info className="ml-2 h-4 w-4" />
                        </TooltipTrigger>

                        <TooltipContent className="max-w-xs text-muted-foreground">
                          <Trans>
                            This document has already been sent to this recipient. You can no longer edit this
                            recipient.
                          </Trans>
                        </TooltipContent>
                      </Tooltip>
                    )}
                  </div>
                </CommandItem>
              ))}
            </CommandGroup>
          ))}
        </Command>
      </PopoverContent>
    </Popover>
  );
};
