import { useState, useMemo } from "react"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { DndContext, closestCenter, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors, type DragEndEvent } from "@dnd-kit/core"
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable"
import { CSS } from "@dnd-kit/utilities"
import { listRepos, deleteRepo, updateRepoOrder } from "@/api/repos"
import { fetchReposGitStatus } from "@/api/git"
import { DeleteDialog } from "@/components/ui/delete-dialog"
import { GitBranch, Search, GripVertical } from "lucide-react"
import type { Repo } from "@/api/types"
import type { GitStatusResponse } from "@/types/git"
import { RepoCard } from "./RepoCard"
import { RepoCardSkeleton } from "./RepoCardSkeleton"
import { useMobile } from "@/hooks/useMobile"
import { useSettings } from "@/hooks/useSettings"
import {
  buildRepoViewModels,
  filterReposBySearch,
  filterReposByMode,
  sortRepos,
  groupReposIntoSections,
  countAttentionItems,
  type RepoFilterMode,
  type RepoSortMode,
} from "./repo-list-state"
import { RepoListControls } from "./RepoListControls"

function formatActivityLabel(timestamp: number): string {
  const now = Date.now()
  const diff = now - timestamp
  const seconds = Math.floor(diff / 1000)
  const minutes = Math.floor(seconds / 60)
  const hours = Math.floor(minutes / 60)
  const days = Math.floor(hours / 24)

  if (days > 0) {
    return days === 1 ? '1d ago' : `${days}d ago`
  }
  if (hours > 0) {
    return hours === 1 ? '1h ago' : `${hours}h ago`
  }
  if (minutes > 0) {
    return minutes === 1 ? '1m ago' : `${minutes}m ago`
  }
  return 'just now'
}

interface RepoCardWrapperProps {
  repo: Repo
  onDelete: (id: number) => void
  isDeleting: boolean
  isSelected: boolean
  onSelect: (id: number, selected: boolean) => void
  gitStatus?: GitStatusResponse
  manageMode: boolean
  isMobile: boolean
  activityLabel?: string
  hasSelectedRepos?: boolean
  selectionMode?: boolean
}

function SortableRepoCard({
  repo,
  onDelete,
  isDeleting,
  isSelected,
  onSelect,
  gitStatus,
  manageMode,
  isMobile,
  activityLabel,
  hasSelectedRepos,
  selectionMode,
  isManualSort,
}: RepoCardWrapperProps & { isManualSort: boolean }) {
  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({ id: repo.id })

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
  }

  return (
    <div ref={setNodeRef} style={style}>
      <div className="relative">
        {isManualSort && (
          <div
            ref={setActivatorNodeRef}
            {...listeners}
            {...attributes}
            className="absolute left-0 top-1/2 -translate-y-1/2 z-10 cursor-grab active:cursor-grabbing touch-none p-1 rounded hover:bg-accent/80"
          >
            <GripVertical className="w-4 h-4 text-muted-foreground" />
          </div>
        )}
        <div className={isManualSort ? 'pl-8' : ''}>
          <RepoCard
            repo={repo}
            onDelete={onDelete}
            isDeleting={isDeleting}
            isSelected={isSelected}
            onSelect={onSelect}
            gitStatus={gitStatus}
            manageMode={manageMode}
            isMobile={isMobile}
            activityLabel={activityLabel}
            hasSelectedRepos={hasSelectedRepos}
            selectionMode={selectionMode}
          />
        </div>
      </div>
    </div>
  )
}

function StaticRepoCard({
  repo,
  onDelete,
  isDeleting,
  isSelected,
  onSelect,
  gitStatus,
  manageMode,
  isMobile,
  activityLabel,
  hasSelectedRepos,
  selectionMode,
}: RepoCardWrapperProps) {
  return (
    <RepoCard
      repo={repo}
      onDelete={onDelete}
      isDeleting={isDeleting}
      isSelected={isSelected}
      onSelect={onSelect}
      gitStatus={gitStatus}
      manageMode={manageMode}
      isMobile={isMobile}
      activityLabel={activityLabel}
      hasSelectedRepos={hasSelectedRepos}
      selectionMode={selectionMode}
    />
  )
}

export function RepoList() {
  const queryClient = useQueryClient()
  const isMobile = useMobile()
  const { preferences, updateSettings } = useSettings()
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
  const [repoToDelete, setRepoToDelete] = useState<number | null>(null)
  const [selectedRepos, setSelectedRepos] = useState<Set<number>>(new Set())
  const [selectionMode, setSelectionMode] = useState(false)
  const [searchQuery, setSearchQuery] = useState("")
  const [filterMode, setFilterMode] = useState<RepoFilterMode>('recent')
  const isSelectionActive = selectionMode || selectedRepos.size > 0

  const sortMode = (preferences?.repoSortMode as RepoSortMode) || 'recent'
  const repoOrder = preferences?.repoOrder

  const handleSortModeChange = (newSortMode: RepoSortMode) => {
    updateSettings({ repoSortMode: newSortMode })
  }

  const isManualSort = sortMode === 'manual'
  const isDragEnabled = !isMobile || (isManualSort && selectedRepos.size === 0)

  const {
    data: repos,
    isLoading,
    error,
  } = useQuery({
    queryKey: ["repos"],
    queryFn: listRepos,
  })

  const repoForDelete = useMemo(() => {
    return repoToDelete ? repos?.find(r => r.id === repoToDelete) : null
  }, [repoToDelete, repos])

  const { hasLocalRepos, hasClonedRepos } = useMemo(() => {
    if (!repos) return { hasLocalRepos: false, hasClonedRepos: false }
    const selectedRepoObjects = repos.filter(r => selectedRepos.has(r.id))
    return {
      hasLocalRepos: selectedRepoObjects.some(r => r.isLocal),
      hasClonedRepos: selectedRepoObjects.some(r => !r.isLocal),
    }
  }, [selectedRepos, repos])

  const repoIds = repos?.map((repo) => repo.id) || []

  const { data: gitStatuses } = useQuery({
    queryKey: ["reposGitStatus", repoIds],
    queryFn: () => fetchReposGitStatus(repoIds),
    enabled: repoIds.length > 0,
    staleTime: 60 * 60 * 1000,
    gcTime: 60 * 60 * 1000,
  })

  const viewModels = useMemo(() => {
    if (!repos) return []
    return buildRepoViewModels(repos, gitStatuses)
  }, [repos, gitStatuses])

  const filteredViewModels = useMemo(() => {
    const searched = filterReposBySearch(viewModels, searchQuery)
    return filterReposByMode(searched, filterMode)
  }, [viewModels, searchQuery, filterMode])

  const sortedViewModels = useMemo(() => {
    return sortRepos(filteredViewModels, sortMode, repoOrder)
  }, [filteredViewModels, sortMode, repoOrder])

  const sections = useMemo(() => {
    return groupReposIntoSections(sortedViewModels, filterMode, sortMode)
  }, [sortedViewModels, filterMode, sortMode])

  const attentionCount = useMemo(() => {
    return countAttentionItems(viewModels)
  }, [viewModels])

  const deleteMutation = useMutation({
    mutationFn: deleteRepo,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["repos"] })
      queryClient.invalidateQueries({ queryKey: ["reposGitStatus"] })
      setDeleteDialogOpen(false)
      setRepoToDelete(null)
    },
  })

  const batchDeleteMutation = useMutation({
    mutationFn: async (repoIds: number[]) => {
      await Promise.all(repoIds.map((id) => deleteRepo(id)))
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["repos"] })
      queryClient.invalidateQueries({ queryKey: ["reposGitStatus"] })
      setDeleteDialogOpen(false)
      setSelectedRepos(new Set())
      setSelectionMode(false)
    },
  })

  const updateOrderMutation = useMutation({
    mutationFn: updateRepoOrder,
    onMutate: async (newOrder) => {
      await queryClient.cancelQueries({ queryKey: ["repos"] })

      const previousRepos = queryClient.getQueryData<Repo[]>(["repos"])

      queryClient.setQueryData<Repo[]>(["repos"], (old) => {
        if (!old) return old
        const repoMap = new Map(old.map((repo) => [repo.id, repo]))
        const reorderedRepos = newOrder.map((id) => repoMap.get(id)).filter((repo): repo is Repo => repo !== undefined)
        const newRepos = old.filter((repo) => !newOrder.includes(repo.id))
        return [...reorderedRepos, ...newRepos]
      })

      return { previousRepos }
    },
    onError: (_error, _variables, context) => {
      queryClient.setQueryData(["repos"], context?.previousRepos)
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ["repos"] })
      queryClient.invalidateQueries({ queryKey: ["reposGitStatus"] })
    },
  })

  const sensors = useSensors(
    useSensor(MouseSensor, {
      activationConstraint: {
        distance: 5,
      },
    }),
    useSensor(TouchSensor, {
      activationConstraint: {
        delay: 200,
        tolerance: 8,
      },
    }),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  )

  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event

    if (!repos || !over) return

    if (active.id !== over.id) {
      const oldIndex = repos.findIndex((repo) => repo.id === Number(active.id))
      const newIndex = repos.findIndex((repo) => repo.id === Number(over.id))

      if (oldIndex === -1 || newIndex === -1) return

      const newOrder = arrayMove(repos, oldIndex, newIndex).map((repo) => repo.id)
      updateOrderMutation.mutate(newOrder)
    }

  }

  const renderContent = () => {
    switch (true) {
      case isLoading && !repos:
        return (
          <div className="px-0 md:p-4 h-full flex flex-col">
            <div className="px-2 md:px-0">
              <div className="h-10 bg-muted/50 animate-pulse rounded w-full" />
            </div>
            <div className="mx-2 md:mx-0 flex-1 min-h-0">
              <div className="h-full overflow-y-auto pt-4 pb-2 md:pb-0">
                <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-3 md:gap-4 w-full">
                  {Array.from({ length: 6 }).map((_, i) => (
                    <div key={i} className="md:pl-8">
                      <RepoCardSkeleton />
                    </div>
                  ))}
                </div>
              </div>
            </div>
          </div>
        )

      case !!error:
        return (
          <div className="text-center p-8 text-destructive">
            Failed to load repositories:{" "}
            {error instanceof Error ? error.message : "Unknown error"}
          </div>
        )

      case !repos || repos.length === 0:
        return (
          <div className="text-center p-12">
            <GitBranch className="w-12 h-12 mx-auto mb-4 text-zinc-600" />
            <p className="text-zinc-500">
              No repositories yet. Add one to get started.
            </p>
          </div>
        )

      default:
        return null
    }
  }

  const content = renderContent()
  if (content) return content

  const handleSelectRepo = (id: number, selected: boolean) => {
    const newSelected = new Set(selectedRepos)
    if (selected) {
      newSelected.add(id)
    } else {
      newSelected.delete(id)
    }
    setSelectedRepos(newSelected)
  }

  const handleSelectAll = () => {
    const visibleIds = sortedViewModels.map(r => r.id)
    const allVisibleSelected = visibleIds.length > 0 && visibleIds.every(id => selectedRepos.has(id))
    
    if (allVisibleSelected) {
      // Unselect only the visible repos, keep hidden ones
      const newSelected = new Set(selectedRepos)
      visibleIds.forEach(id => newSelected.delete(id))
      setSelectedRepos(newSelected)
    } else {
      // Add all visible repos to existing selection (preserves hidden selections)
      const newSelected = new Set(selectedRepos)
      visibleIds.forEach(id => newSelected.add(id))
      setSelectedRepos(newSelected)
    }
  }

  const handleSelectionModeChange = (enabled: boolean) => {
    setSelectionMode(enabled)
    if (!enabled) {
      setSelectedRepos(new Set())
    }
  }

  return (
    <>
      <div className="px-0 md:p-4 h-full flex flex-col">
        <RepoListControls
          searchQuery={searchQuery}
          onSearchChange={setSearchQuery}
          filterMode={filterMode}
          onFilterModeChange={setFilterMode}
          sortMode={sortMode}
          onSortModeChange={handleSortModeChange}
          filteredCount={sortedViewModels.length}
          attentionCount={attentionCount}
          selectedCount={selectedRepos.size}
          allVisibleSelected={sortedViewModels.length > 0 && sortedViewModels.every(r => selectedRepos.has(r.id))}
          onSelectAll={handleSelectAll}
          onClearSelection={() => {
            setSelectedRepos(new Set())
            setSelectionMode(false)
          }}
          onDelete={() => {
            setRepoToDelete(null)
            setDeleteDialogOpen(true)
          }}
          hasLocalRepos={hasLocalRepos}
          hasClonedRepos={hasClonedRepos}
          selectionMode={selectionMode}
          onSelectionModeChange={handleSelectionModeChange}
        />

        <div className="mx-2 md:mx-0 flex-1 min-h-0">
          <div className="h-full overflow-y-auto pt-4 md:pb-0 [mask-image:linear-gradient(to_bottom,transparent,black_16px,black)]">
            {(() => {
              switch (true) {
                case sortedViewModels.length === 0:
                  return (
                    <div className="text-center p-12">
                      <Search className="w-12 h-12 mx-auto mb-4 text-zinc-600" />
                      <p className="text-zinc-500">
                        {sections[0]?.emptyMessage || `No repositories found${searchQuery ? ` matching "${searchQuery}"` : ''}`}
                      </p>
                    </div>
                  )

                case isDragEnabled:
                  return (
                    <DndContext
                      sensors={sensors}
                      collisionDetection={closestCenter}
                      onDragEnd={handleDragEnd}
                    >
                      <SortableContext items={sortedViewModels.map((r) => r.id)} strategy={verticalListSortingStrategy}>
                        <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-3 md:gap-4 w-full md:pb-0">
                          {sortedViewModels.map((repo) => (
                            <SortableRepoCard
                              key={repo.id}
                              repo={repo}
                              onDelete={(id) => {
                                setRepoToDelete(id)
                                setDeleteDialogOpen(true)
                              }}
                              isDeleting={
                                deleteMutation.isPending && repoToDelete === repo.id
                              }
                              isSelected={selectedRepos.has(repo.id)}
                              onSelect={handleSelectRepo}
                              gitStatus={gitStatuses?.get(repo.id)}
                              manageMode={isSelectionActive}
                              isMobile={isMobile}
                              isManualSort={isManualSort}
                              activityLabel={formatActivityLabel(repo.activityTimestamp)}
                              hasSelectedRepos={selectedRepos.size > 0}
                              selectionMode={selectionMode}
                            />
                          ))}
                        </div>
                      </SortableContext>
                    </DndContext>
                  )

                default:
                  return (
                    <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-3 md:gap-4 w-full md:pb-0">
                      {sortedViewModels.map((repo) => (
                        <StaticRepoCard
                          key={repo.id}
                          repo={repo}
                          onDelete={(id) => {
                            setRepoToDelete(id)
                            setDeleteDialogOpen(true)
                          }}
                          isDeleting={
                            deleteMutation.isPending && repoToDelete === repo.id
                          }
                          isSelected={selectedRepos.has(repo.id)}
                          onSelect={handleSelectRepo}
                          gitStatus={gitStatuses?.get(repo.id)}
                          manageMode={isSelectionActive}
                          isMobile={isMobile}
                          activityLabel={formatActivityLabel(repo.activityTimestamp)}
                          hasSelectedRepos={selectedRepos.size > 0}
                          selectionMode={selectionMode}
                        />
                      ))}
                    </div>
                  )
              }
            })()}
          </div>
        </div>
      </div>

      <DeleteDialog
        open={deleteDialogOpen}
        onOpenChange={setDeleteDialogOpen}
        onConfirm={() => {
          if (selectedRepos.size > 0) {
            batchDeleteMutation.mutate(Array.from(selectedRepos))
          } else if (repoToDelete) {
            deleteMutation.mutate(repoToDelete)
          }
        }}
        onCancel={() => {
          setDeleteDialogOpen(false)
          setRepoToDelete(null)
          if (selectedRepos.size > 0) {
            setSelectedRepos(new Set())
            setSelectionMode(false)
          }
        }}
        title={
          selectedRepos.size > 0
            ? hasLocalRepos && !hasClonedRepos
              ? "Unlink Multiple Repositories"
              : "Delete Multiple Repositories"
            : repoForDelete
              ? repoForDelete.isLocal
                ? "Unlink Repository"
                : "Delete Repository"
              : "Delete Repository"
        }
        description={
          selectedRepos.size > 0
            ? hasClonedRepos && !hasLocalRepos
              ? `Are you sure you want to delete ${selectedRepos.size} repositor${selectedRepos.size === 1 ? "y" : "ies"}? This will remove all local files. This action cannot be undone.`
              : hasLocalRepos && !hasClonedRepos
                ? `Are you sure you want to unlink ${selectedRepos.size} repositor${selectedRepos.size === 1 ? "y" : "ies"}? Only workspace references will be removed. Your original files will not be affected.`
                : `Are you sure you want to delete ${selectedRepos.size} repositor${selectedRepos.size === 1 ? "y" : "ies"}? Cloned repositories will have their local files removed. Locally discovered repositories will only have their workspace references removed — original files will not be affected.`
            : repoForDelete?.isLocal
              ? (
                <>
                  Are you sure you want to unlink this repository? Only the workspace reference will be removed.
                  {repoForDelete.sourcePath && (
                    <>
                      {" "}Your original files at{" "}
                      <span className="font-mono text-xs">{repoForDelete.sourcePath}</span>{" "}
                      will not be affected.
                    </>
                  )}
                </>
              )
              : "Are you sure you want to delete this repository? This will remove all local files. This action cannot be undone."
        }
        isDeleting={deleteMutation.isPending || batchDeleteMutation.isPending}
      />
    </>
  )
}
