import { useEffect, useMemo, useState } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { Users, Eye, Pencil, X, Loader2 } from 'lucide-react'
import { cn } from '@/lib/utils'
import {
  sharing,
  type ShareInfo,
  type ShareableUser,
} from '../../services/api'
import { formatApiError } from '../../lib/apiError'
import {
  SHARED_HEAPS_KEY,
  SHARED_FOLDERS_KEY,
} from '../../hooks/useSharingQueries'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Separator } from '@/components/ui/separator'

interface ShareDialogProps {
  isOpen: boolean
  type: 'heap' | 'folder'
  targetId: string
  targetName: string
  onClose: () => void
}

export function ShareDialog({
  isOpen,
  type,
  targetId,
  targetName,
  onClose,
}: ShareDialogProps) {
  const [username, setUsername] = useState('')
  const [permission, setPermission] = useState<'read' | 'write'>('read')
  const [error, setError] = useState<string | null>(null)
  const queryClient = useQueryClient()

  const sharesQueryKey = [type, 'shares', targetId]

  const { data: shares = [], isLoading } = useQuery<ShareInfo[]>({
    queryKey: sharesQueryKey,
    queryFn: () =>
      type === 'heap'
        ? sharing.heapShares(targetId)
        : sharing.folderShares(targetId),
    enabled: isOpen,
  })

  // All users that can be shared with (the caller is filtered out
  // server-side). Only fetched while the dialog is open. Cached for 60s
  // because the user directory changes slowly.
  const { data: allUsers = [], isLoading: isLoadingUsers } = useQuery<
    ShareableUser[]
  >({
    queryKey: ['sharing', 'users'],
    queryFn: sharing.listUsers,
    enabled: isOpen,
    staleTime: 60_000,
  })

  // Users who don't already have a share on this target.
  const availableUsers = useMemo(() => {
    const taken = new Set(shares.map((s) => s.shared_with_username))
    return allUsers.filter((u) => !taken.has(u.username))
  }, [allUsers, shares])

  const addMutation = useMutation({
    mutationFn: () =>
      type === 'heap'
        ? sharing.shareHeap(targetId, username, permission)
        : sharing.shareFolder(targetId, username, permission),
    onSuccess: () => {
      setUsername('')
      setPermission('read')
      setError(null)
      queryClient.invalidateQueries({ queryKey: sharesQueryKey })
      queryClient.invalidateQueries({
        queryKey: type === 'heap' ? SHARED_HEAPS_KEY : SHARED_FOLDERS_KEY,
      })
    },
    onError: (err) => {
      setError(formatApiError(err, 'Failed to share'))
    },
  })

  const revokeMutation = useMutation({
    mutationFn: (shareId: string) =>
      type === 'heap'
        ? sharing.revokeHeapShare(targetId, shareId)
        : sharing.revokeFolderShare(targetId, shareId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: sharesQueryKey })
      queryClient.invalidateQueries({
        queryKey: type === 'heap' ? SHARED_HEAPS_KEY : SHARED_FOLDERS_KEY,
      })
    },
  })

  useEffect(() => {
    if (isOpen) {
      setUsername('')
      setPermission('read')
      setError(null)
    }
  }, [isOpen])

  const invitePlaceholder = isLoadingUsers
    ? 'Loading users…'
    : availableUsers.length === 0
      ? shares.length > 0
        ? 'Everyone already has access'
        : 'No users to share with'
      : 'Add people…'

  return (
    <Dialog open={isOpen} onOpenChange={(o) => !o && onClose()}>
      {/* flex flex-col so the gap-4 between header → invite row →
       *  separator → list actually applies (DialogContent isn't a flex
       *  container by default, which is what made the spacing feel
       *  random on the previous pass). */}
      <DialogContent className="flex max-w-md flex-col gap-4">
        {/* Header — target name inlines into the title so there's no
         *  separate "chip" container that reads like an empty input.
         *  Google Drive / Notion / Linear all follow this pattern. */}
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2 pr-6">
            <Users className="h-4 w-4 shrink-0 text-primary" />
            <span className="truncate">
              Share
              <span className="ml-1 font-normal text-text-muted">
                &ldquo;{targetName}&rdquo;
              </span>
            </span>
          </DialogTitle>
          <DialogDescription>
            Invite people to view or edit this {type}.
          </DialogDescription>
        </DialogHeader>

        {/* Invite row — single compact line: user picker (flex-1) +
         *  permission + primary action. Mirrors the Drive/Notion
         *  invitation bar, where the whole flow is reachable without
         *  scanning multiple labeled sections. */}
        <form
          onSubmit={(e) => {
            e.preventDefault()
            if (username.trim()) addMutation.mutate()
          }}
          className="space-y-2"
        >
          <div className="flex gap-2">
            <Select
              value={username}
              onValueChange={(v) => {
                setUsername(v)
                setError(null)
              }}
              disabled={isLoadingUsers || availableUsers.length === 0}
            >
              <SelectTrigger className="flex-1">
                <SelectValue placeholder={invitePlaceholder} />
              </SelectTrigger>
              <SelectContent>
                {availableUsers.map((u) => (
                  <SelectItem key={u.id} value={u.username}>
                    <span className="flex items-center gap-2">
                      <Avatar name={u.username} size="sm" />
                      {u.username}
                    </span>
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Select
              value={permission}
              onValueChange={(v) => setPermission(v as 'read' | 'write')}
            >
              <SelectTrigger className="w-[112px]">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="read">
                  <span className="flex items-center gap-2">
                    <Eye className="h-3.5 w-3.5" />
                    Viewer
                  </span>
                </SelectItem>
                <SelectItem value="write">
                  <span className="flex items-center gap-2">
                    <Pencil className="h-3.5 w-3.5" />
                    Editor
                  </span>
                </SelectItem>
              </SelectContent>
            </Select>
            <Button
              type="submit"
              disabled={!username.trim() || addMutation.isPending}
            >
              {addMutation.isPending ? (
                <Loader2 className="h-4 w-4 animate-spin" />
              ) : (
                'Share'
              )}
            </Button>
          </div>
          {error && (
            <Alert variant="destructive">
              <AlertDescription>{error}</AlertDescription>
            </Alert>
          )}
        </form>

        <Separator />

        {/* People list — hoverable rows, each with avatar + name +
         *  permission subtitle + X to revoke. This is the same layout
         *  Drive/Notion use: one primary line per person, permission
         *  relegated to a subtle subtitle rather than a loud pill. */}
        <section className="space-y-2">
          <div className="flex items-baseline justify-between">
            <h3 className="text-sm font-medium text-text">People with access</h3>
            {shares.length > 0 && (
              <span className="text-[11px] text-text-faint">
                {shares.length}
              </span>
            )}
          </div>

          {isLoading ? (
            <div className="flex items-center gap-2 px-1 py-2 text-xs text-text-muted">
              <Loader2 className="h-3.5 w-3.5 animate-spin" />
              Loading…
            </div>
          ) : shares.length === 0 ? (
            <p className="px-1 py-1 text-xs text-text-muted">
              Only you can access this {type}.
            </p>
          ) : (
            <ul className="-mx-2">
              {shares.map((share) => (
                <li
                  key={share.id}
                  className="group flex items-center gap-3 rounded-md px-2 py-1.5 hover:bg-surface-2"
                >
                  <Avatar name={share.shared_with_username} />
                  <div className="min-w-0 flex-1">
                    <div className="truncate text-sm font-medium text-text">
                      {share.shared_with_username}
                    </div>
                    <div className="flex items-center gap-1 text-[11px] text-text-muted">
                      {share.permission === 'write' ? (
                        <>
                          <Pencil className="h-2.5 w-2.5" />
                          Can edit
                        </>
                      ) : (
                        <>
                          <Eye className="h-2.5 w-2.5" />
                          Can view
                        </>
                      )}
                    </div>
                  </div>
                  <Button
                    variant="ghost"
                    size="icon"
                    onClick={() => revokeMutation.mutate(share.id)}
                    disabled={revokeMutation.isPending}
                    className={cn(
                      'h-7 w-7 text-text-muted transition-opacity hover:bg-reject/10 hover:text-reject',
                      // Hover-revealed on pointer devices, always
                      // visible on touch (focus-within covers keyboard
                      // nav as well).
                      'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100'
                    )}
                    title="Revoke access"
                  >
                    <X className="h-3.5 w-3.5" />
                  </Button>
                </li>
              ))}
            </ul>
          )}
        </section>
      </DialogContent>
    </Dialog>
  )
}

// ── Helpers ────────────────────────────────────────────────────────────

/** Circular initial bubble. Hashes the username into one of a small
 *  set of stable palette tints so each person's avatar reads
 *  consistently across the app — the colour isn't meaningful, just an
 *  identity cue that makes a list of names feel less anonymous. */
function Avatar({ name, size = 'md' }: { name: string; size?: 'sm' | 'md' }) {
  const initials = name.slice(0, 2).toUpperCase()
  const palette = [
    'bg-primary/25 text-primary',
    'bg-pick/25 text-pick',
    'bg-blue-500/25 text-blue-300',
    'bg-purple-500/25 text-purple-300',
    'bg-amber-500/25 text-amber-300',
  ]
  let hash = 0
  for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0
  const tint = palette[Math.abs(hash) % palette.length]
  const dims = size === 'sm' ? 'h-5 w-5 text-[9px]' : 'h-8 w-8 text-[11px]'
  return (
    <span
      className={cn(
        'inline-flex shrink-0 items-center justify-center rounded-full font-semibold uppercase tracking-wide',
        dims,
        tint
      )}
      aria-hidden
    >
      {initials}
    </span>
  )
}
