import { keepPreviousData } from '@tanstack/react-query'
import { useDebounce, useIntersectionObserver } from '@uidotdev/usehooks'
import { ChevronsUpDown, HelpCircle } from 'lucide-react'
import { ReactNode, useEffect, useId, useMemo, useRef, useState } from 'react'
import {
  Button,
  cn,
  Command,
  CommandGroup,
  CommandInput,
  CommandList,
  Popover,
  PopoverContent,
  PopoverTrigger,
  ScrollArea,
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'

import { EmbeddedProjectList } from './OrganizationProjectSelector/EmbeddedProjectList'
import { ProjectCommandItem } from './OrganizationProjectSelector/ProjectCommandItem'
import {
  OrgProject,
  useOrgProjectsInfiniteQuery,
} from '@/data/projects/org-projects-infinite-query'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'

interface OrganizationProjectSelectorSelectorProps {
  slug?: string
  open?: boolean
  selectedRef?: string | null
  searchPlaceholder?: string
  sameWidthAsTrigger?: boolean
  checkPosition?: 'right' | 'left'
  setOpen?: (value: boolean) => void
  renderRow?: (project: OrgProject) => ReactNode
  renderTrigger?: ({
    isLoading,
    project,
    listboxId,
    open,
  }: {
    isLoading: boolean
    project?: OrgProject
    listboxId: string
    open: boolean
  }) => ReactNode
  renderActions?: (setOpen: (value: boolean) => void, options?: { embedded?: boolean }) => ReactNode
  onSelect?: (project: OrgProject) => void
  onInitialLoad?: (projects: OrgProject[]) => void
  isOptionDisabled?: (project: OrgProject) => boolean
  fetchOnMount?: boolean
  modal?: boolean
  /** When true, render only the command list (no popover/trigger). For use inside sheet or popover. */
  embedded?: boolean
  className?: string
}

export const OrganizationProjectSelector = ({
  slug: _slug,
  open: _open,
  setOpen: _setOpen,
  selectedRef,
  searchPlaceholder = 'Find project...',
  sameWidthAsTrigger = false,
  checkPosition = 'right',
  renderRow,
  renderTrigger,
  renderActions,
  onSelect,
  onInitialLoad,
  isOptionDisabled,
  fetchOnMount = false,
  modal = false,
  embedded = false,
  className,
}: OrganizationProjectSelectorSelectorProps) => {
  const { data: organization } = useSelectedOrganizationQuery()
  const slug = _slug ?? organization?.slug

  const [openInternal, setOpenInternal] = useState(false)
  const open = _open ?? openInternal
  const setOpen = _setOpen ?? setOpenInternal
  const listboxId = useId()

  const [search, setSearch] = useState('')
  const debouncedSearch = useDebounce(search, 500)

  const scrollRootRef = useRef<HTMLDivElement | null>(null)
  const [sentinelRef, entry] = useIntersectionObserver({
    root: scrollRootRef.current,
    threshold: 0,
    rootMargin: '0px',
  })

  const {
    data,
    error: projectsError,
    isLoading: isLoadingProjects,
    isError: isErrorProjects,
    isSuccess: isSuccessProjects,
    isFetching,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage,
  } = useOrgProjectsInfiniteQuery(
    { slug, search: search.length === 0 ? search : debouncedSearch },
    { enabled: fetchOnMount || open, placeholderData: keepPreviousData }
  )

  const projects = useMemo(() => data?.pages.flatMap((page) => page.projects), [data?.pages]) || []
  const selectedProject = projects.find((p) => p.ref === selectedRef)

  useEffect(() => {
    if (
      !isLoadingProjects &&
      !isFetching &&
      entry?.isIntersecting &&
      hasNextPage &&
      !isFetchingNextPage
    ) {
      fetchNextPage()
    }
  }, [
    entry?.isIntersecting,
    hasNextPage,
    isFetching,
    isFetchingNextPage,
    isLoadingProjects,
    fetchNextPage,
  ])

  useEffect(() => {
    // isLoadingProjects is true only during initial load. If the variables for the query change (slug), isLoadingProjects
    // will be true again.
    if (!isLoadingProjects && isSuccessProjects) {
      onInitialLoad?.(projects)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isLoadingProjects, isSuccessProjects])

  function renderListContent() {
    if (isLoadingProjects) {
      return (
        <>
          <div className="px-2 py-1">
            <ShimmeringLoader className="py-2" />
          </div>
          <div className="px-2 py-1 w-4/5">
            <ShimmeringLoader className="py-2" />
          </div>
        </>
      )
    }
    if (isErrorProjects) {
      return (
        <div className="flex items-center gap-x-2 py-3 justify-center">
          <p className="text-xs text-foreground-lighter">Failed to retrieve projects</p>
          <Tooltip>
            <TooltipTrigger>
              <HelpCircle size={14} />
            </TooltipTrigger>
            <TooltipContent side="bottom">Error: {projectsError?.message}</TooltipContent>
          </Tooltip>
        </div>
      )
    }
    if (search.length > 0 && projects.length === 0) {
      return (
        <p className="text-xs text-center text-foreground-lighter py-3">
          No projects found based on your search
        </p>
      )
    }
    if (projects.length === 0) {
      return <p className="text-xs text-center text-foreground-lighter py-3">No projects found</p>
    }
    if (embedded) {
      return (
        <EmbeddedProjectList
          projects={projects}
          selectedRef={selectedRef ?? undefined}
          onSelect={onSelect}
          onClose={() => setOpen(false)}
          renderRow={renderRow}
          checkPosition={checkPosition}
          isOptionDisabled={isOptionDisabled}
          sentinelRef={sentinelRef}
          hasNextPage={!!hasNextPage}
        />
      )
    }
    return (
      <ScrollArea className={(projects || []).length > 7 ? 'h-full md:h-[210px]' : ''}>
        {projects?.map((project) => (
          <ProjectCommandItem
            key={project.ref}
            project={project}
            selectedRef={selectedRef ?? undefined}
            onSelect={onSelect}
            onClose={() => setOpen(false)}
            renderRow={renderRow}
            checkPosition={checkPosition}
            isOptionDisabled={isOptionDisabled}
          />
        ))}
        <div ref={sentinelRef} className="h-1 -mt-1" />
        {hasNextPage && (
          <div className="px-2 py-1">
            <ShimmeringLoader className="py-2" />
          </div>
        )}
      </ScrollArea>
    )
  }

  const commandContent = (
    <Command
      shouldFilter={false}
      className={cn(className, embedded && 'flex flex-col flex-1 min-h-0 overflow-hidden')}
    >
      {embedded && !!renderActions && (
        <div className="flex items-center gap-2 shrink-0 border-b p-2">
          {renderActions(setOpen, { embedded: true })}
        </div>
      )}
      <CommandInput
        showResetIcon
        value={search}
        onValueChange={setSearch}
        placeholder={searchPlaceholder}
        handleReset={() => setSearch('')}
        wrapperClassName={embedded ? 'shrink-0 border-b' : undefined}
        className="text-base sm:text-sm"
      />
      <CommandList
        className={
          embedded
            ? 'flex-1 min-h-0 overflow-y-auto overflow-x-hidden max-h-none!'
            : 'max-h-none md:max-h-[300px] overflow-y-auto overflow-x-hidden'
        }
      >
        <CommandGroup className={embedded ? 'flex-1 min-h-0 overflow-hidden' : ''}>
          {renderListContent()}
        </CommandGroup>
        {!!renderActions && !embedded && (
          <>
            <div className="h-px bg-border-overlay -mx-1 shrink-0" />
            {renderActions(setOpen)}
          </>
        )}
      </CommandList>
    </Command>
  )

  if (embedded) {
    return commandContent
  }

  return (
    <Popover open={open} onOpenChange={setOpen} modal={modal}>
      <PopoverTrigger asChild>
        {renderTrigger ? (
          renderTrigger({
            isLoading: isLoadingProjects || isFetching,
            project: selectedProject,
            listboxId,
            open,
          })
        ) : (
          <Button
            block
            type="default"
            role="combobox"
            size="small"
            aria-expanded={open}
            aria-controls={listboxId}
            className="justify-between"
            iconRight={<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />}
          >
            {isLoadingProjects || isFetching ? (
              <ShimmeringLoader className="w-44 py-2" />
            ) : (
              (selectedProject?.name ?? 'Select a project')
            )}
          </Button>
        )}
      </PopoverTrigger>
      <PopoverContent
        id={listboxId}
        sameWidthAsTrigger={sameWidthAsTrigger}
        className="p-0"
        side="bottom"
        align="start"
      >
        {commandContent}
      </PopoverContent>
    </Popover>
  )
}
