/* Memories */
import { useMemo, useState, useRef, useEffect } from 'react';
import { Plus } from 'lucide-react';
import { matchSorter } from 'match-sorter';
import { SystemRoles, PermissionTypes, Permissions } from 'librechat-data-provider';
import type { TUserMemory } from 'librechat-data-provider';
import {
  Spinner,
  EditIcon,
  TrashIcon,
  Table,
  Input,
  Label,
  Button,
  Switch,
  TableRow,
  OGDialog,
  TableHead,
  TableBody,
  TableCell,
  TableHeader,
  TooltipAnchor,
  OGDialogTrigger,
} from '~/components';
import {
  useGetUserQuery,
  useMemoriesQuery,
  useDeleteMemoryMutation,
  useUpdateMemoryPreferencesMutation,
} from '~/data-provider';
import { useLocalize, useAuthContext, useHasAccess } from '~/hooks';
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
import MemoryCreateDialog from './MemoryCreateDialog';
import MemoryEditDialog from './MemoryEditDialog';
import { useToastContext } from '~/Providers';
import AdminSettings from './AdminSettings';
import { cn } from '~/utils';

export default function MemoryViewer() {
  const localize = useLocalize();
  const { user } = useAuthContext();
  const { data: userData } = useGetUserQuery();
  const { data: memData, isLoading } = useMemoriesQuery();
  const { mutate: deleteMemory } = useDeleteMemoryMutation();
  const { showToast } = useToastContext();
  const [pageIndex, setPageIndex] = useState(0);
  const [searchQuery, setSearchQuery] = useState('');
  const pageSize = 10;
  const [createDialogOpen, setCreateDialogOpen] = useState(false);
  const [deletingKey, setDeletingKey] = useState<string | null>(null);
  const [referenceSavedMemories, setReferenceSavedMemories] = useState(true);

  const updateMemoryPreferencesMutation = useUpdateMemoryPreferencesMutation({
    onSuccess: () => {
      showToast({
        message: localize('com_ui_preferences_updated'),
        status: 'success',
      });
    },
    onError: () => {
      showToast({
        message: localize('com_ui_error_updating_preferences'),
        status: 'error',
      });
      setReferenceSavedMemories((prev) => !prev);
    },
  });

  useEffect(() => {
    if (userData?.personalization?.memories !== undefined) {
      setReferenceSavedMemories(userData.personalization.memories);
    }
  }, [userData?.personalization?.memories]);

  const handleMemoryToggle = (checked: boolean) => {
    setReferenceSavedMemories(checked);
    updateMemoryPreferencesMutation.mutate({ memories: checked });
  };

  const hasReadAccess = useHasAccess({
    permissionType: PermissionTypes.MEMORIES,
    permission: Permissions.READ,
  });

  const hasUpdateAccess = useHasAccess({
    permissionType: PermissionTypes.MEMORIES,
    permission: Permissions.UPDATE,
  });

  const hasCreateAccess = useHasAccess({
    permissionType: PermissionTypes.MEMORIES,
    permission: Permissions.CREATE,
  });

  const hasOptOutAccess = useHasAccess({
    permissionType: PermissionTypes.MEMORIES,
    permission: Permissions.OPT_OUT,
  });

  const memories: TUserMemory[] = useMemo(() => memData?.memories ?? [], [memData]);

  const filteredMemories = useMemo(() => {
    return matchSorter(memories, searchQuery, {
      keys: ['key', 'value'],
    });
  }, [memories, searchQuery]);

  const currentRows = useMemo(() => {
    return filteredMemories.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
  }, [filteredMemories, pageIndex]);

  const getProgressBarColor = (percentage: number): string => {
    if (percentage > 90) {
      return 'stroke-red-500';
    }
    if (percentage > 75) {
      return 'stroke-yellow-500';
    }
    return 'stroke-green-500';
  };

  const EditMemoryButton = ({ memory }: { memory: TUserMemory }) => {
    const [open, setOpen] = useState(false);
    const triggerRef = useRef<HTMLDivElement>(null);

    // Only show edit button if user has UPDATE permission
    if (!hasUpdateAccess) {
      return null;
    }

    return (
      <MemoryEditDialog
        open={open}
        memory={memory}
        onOpenChange={setOpen}
        triggerRef={triggerRef as React.MutableRefObject<HTMLButtonElement | null>}
      >
        <OGDialogTrigger asChild>
          <TooltipAnchor
            description={localize('com_ui_edit_memory')}
            render={
              <Button
                variant="ghost"
                aria-label={localize('com_ui_bookmarks_edit')}
                onClick={() => setOpen(!open)}
                className="h-8 w-8 p-0"
              >
                <EditIcon />
              </Button>
            }
          />
        </OGDialogTrigger>
      </MemoryEditDialog>
    );
  };

  const DeleteMemoryButton = ({ memory }: { memory: TUserMemory }) => {
    const [open, setOpen] = useState(false);

    if (!hasUpdateAccess) {
      return null;
    }

    const confirmDelete = async () => {
      setDeletingKey(memory.key);
      deleteMemory(memory.key, {
        onSuccess: () => {
          showToast({
            message: localize('com_ui_deleted'),
            status: 'success',
          });
          setOpen(false);
        },
        onError: () =>
          showToast({
            message: localize('com_ui_error'),
            status: 'error',
          }),
        onSettled: () => setDeletingKey(null),
      });
    };

    return (
      <OGDialog open={open} onOpenChange={setOpen}>
        <OGDialogTrigger asChild>
          <TooltipAnchor
            description={localize('com_ui_delete_memory')}
            render={
              <Button
                variant="ghost"
                aria-label={localize('com_ui_delete')}
                onClick={() => setOpen(!open)}
                className="h-8 w-8 p-0"
              >
                {deletingKey === memory.key ? (
                  <Spinner className="size-4 animate-spin" />
                ) : (
                  <TrashIcon className="size-4" />
                )}
              </Button>
            }
          />
        </OGDialogTrigger>
        <OGDialogTemplate
          showCloseButton={false}
          title={localize('com_ui_delete_memory')}
          className="w-11/12 max-w-lg"
          main={
            <Label className="text-left text-sm font-medium">
              {localize('com_ui_delete_confirm')} &quot;{memory.key}&quot;?
            </Label>
          }
          selection={{
            selectHandler: confirmDelete,
            selectClasses:
              'bg-red-700 dark:bg-red-600 hover:bg-red-800 dark:hover:bg-red-800 text-white',
            selectText: localize('com_ui_delete'),
          }}
        />
      </OGDialog>
    );
  };

  if (isLoading) {
    return (
      <div className="flex h-full w-full items-center justify-center p-4">
        <Spinner />
      </div>
    );
  }

  if (!hasReadAccess) {
    return (
      <div className="flex h-full w-full items-center justify-center p-4">
        <div className="text-center">
          <p className="text-sm text-text-secondary">{localize('com_ui_no_read_access')}</p>
        </div>
      </div>
    );
  }

  return (
    <div className="flex h-full w-full flex-col overflow-hidden">
      <div role="region" aria-label={localize('com_ui_memories')} className="mt-2 space-y-2">
        <div className="flex items-center gap-4">
          <Input
            placeholder={localize('com_ui_memories_filter')}
            value={searchQuery}
            onChange={(e) => setSearchQuery(e.target.value)}
            aria-label={localize('com_ui_memories_filter')}
          />
        </div>
        {/* Memory Usage and Toggle Display */}
        {(memData?.tokenLimit || hasOptOutAccess) && (
          <div
            className={cn(
              'flex items-center rounded-lg',
              memData?.tokenLimit != null && hasOptOutAccess ? 'justify-between' : 'justify-end',
            )}
          >
            {/* Usage Display */}
            {memData?.tokenLimit && (
              <div className="flex items-center gap-2">
                <div className="relative size-10">
                  <svg className="size-10 -rotate-90 transform">
                    <circle
                      cx="20"
                      cy="20"
                      r="16"
                      stroke="currentColor"
                      strokeWidth="3"
                      fill="none"
                      className="text-gray-200 dark:text-gray-700"
                    />
                    <circle
                      cx="20"
                      cy="20"
                      r="16"
                      strokeWidth="3"
                      fill="none"
                      strokeDasharray={`${2 * Math.PI * 16}`}
                      strokeDashoffset={`${2 * Math.PI * 16 * (1 - (memData.usagePercentage ?? 0) / 100)}`}
                      className={`transition-all ${getProgressBarColor(memData.usagePercentage ?? 0)}`}
                      strokeLinecap="round"
                    />
                  </svg>
                  <div className="absolute inset-0 flex items-center justify-center">
                    <span className="text-xs font-medium">{memData.usagePercentage}%</span>
                  </div>
                </div>
                <div className="text-sm text-text-secondary">{localize('com_ui_usage')}</div>
              </div>
            )}

            {/* Memory Toggle */}
            {hasOptOutAccess && (
              <div className="flex items-center gap-2 text-xs">
                <span>{localize('com_ui_use_memory')}</span>
                <Switch
                  checked={referenceSavedMemories}
                  onCheckedChange={handleMemoryToggle}
                  aria-label={localize('com_ui_reference_saved_memories')}
                  disabled={updateMemoryPreferencesMutation.isLoading}
                />
              </div>
            )}
          </div>
        )}
        {/* Create Memory Button */}
        {hasCreateAccess && (
          <div className="flex w-full justify-end">
            <MemoryCreateDialog open={createDialogOpen} onOpenChange={setCreateDialogOpen}>
              <OGDialogTrigger asChild>
                <Button variant="outline" className="w-full bg-transparent">
                  <Plus className="size-4" aria-hidden />
                  {localize('com_ui_create_memory')}
                </Button>
              </OGDialogTrigger>
            </MemoryCreateDialog>
          </div>
        )}
        <div className="rounded-lg border border-border-light bg-transparent shadow-sm transition-colors">
          <Table className="w-full table-fixed">
            <TableHeader>
              <TableRow className="border-b border-border-light hover:bg-surface-secondary">
                <TableHead
                  className={`${
                    hasUpdateAccess ? 'w-[75%]' : 'w-[100%]'
                  } bg-surface-secondary py-3 text-left text-sm font-medium text-text-secondary`}
                >
                  <div>{localize('com_ui_memory')}</div>
                </TableHead>
                {hasUpdateAccess && (
                  <TableHead className="w-[25%] bg-surface-secondary py-3 text-center text-sm font-medium text-text-secondary">
                    <div>{localize('com_assistants_actions')}</div>
                  </TableHead>
                )}
              </TableRow>
            </TableHeader>
            <TableBody>
              {currentRows.length ? (
                currentRows.map((memory: TUserMemory, idx: number) => (
                  <TableRow
                    key={idx}
                    className="border-b border-border-light hover:bg-surface-secondary"
                  >
                    <TableCell className={`${hasUpdateAccess ? 'w-[75%]' : 'w-[100%]'} px-4 py-4`}>
                      <div
                        className="overflow-hidden text-ellipsis whitespace-nowrap text-sm text-text-primary"
                        title={memory.value}
                      >
                        {memory.value}
                      </div>
                    </TableCell>
                    {hasUpdateAccess && (
                      <TableCell className="w-[25%] px-4 py-4">
                        <div className="flex justify-center gap-2">
                          <EditMemoryButton memory={memory} />
                          <DeleteMemoryButton memory={memory} />
                        </div>
                      </TableCell>
                    )}
                  </TableRow>
                ))
              ) : (
                <TableRow>
                  <TableCell
                    colSpan={hasUpdateAccess ? 2 : 1}
                    className="h-24 text-center text-sm text-text-secondary"
                  >
                    {localize('com_ui_no_data')}
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
        </div>

        {/* Pagination controls */}
        {filteredMemories.length > pageSize && (
          <div
            className="flex items-center justify-end gap-2"
            role="navigation"
            aria-label="Pagination"
          >
            <Button
              variant="outline"
              size="sm"
              onClick={() => setPageIndex((prev) => Math.max(prev - 1, 0))}
              disabled={pageIndex === 0}
              aria-label={localize('com_ui_prev')}
            >
              {localize('com_ui_prev')}
            </Button>
            <div className="text-sm" aria-live="polite">
              {`${pageIndex + 1} / ${Math.ceil(filteredMemories.length / pageSize)}`}
            </div>
            <Button
              variant="outline"
              size="sm"
              onClick={() =>
                setPageIndex((prev) =>
                  (prev + 1) * pageSize < filteredMemories.length ? prev + 1 : prev,
                )
              }
              disabled={(pageIndex + 1) * pageSize >= filteredMemories.length}
              aria-label={localize('com_ui_next')}
            >
              {localize('com_ui_next')}
            </Button>
          </div>
        )}

        {/* Admin Settings */}
        {user?.role === SystemRoles.ADMIN && (
          <div className="mt-4">
            <AdminSettings />
          </div>
        )}
      </div>
    </div>
  );
}
