import { useEffect, useState, useCallback } from 'react';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import {
  Archive,
  ChevronDown,
  ChevronRight,
  Clock,
  FileText,
  GitBranch,
  Play,
  Trash2,
} from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { getHttpApiClient } from '@/lib/http-api-client';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';

interface WorktreeInfo {
  path: string;
  branch: string;
  isMain: boolean;
  hasChanges?: boolean;
  changedFilesCount?: number;
}

interface StashEntry {
  index: number;
  message: string;
  branch: string;
  date: string;
  files: string[];
}

interface ViewStashesDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  worktree: WorktreeInfo | null;
  onStashApplied?: () => void;
}

function formatRelativeDate(dateStr: string): string {
  const date = new Date(dateStr);
  if (isNaN(date.getTime())) return 'Unknown date';
  const now = new Date();
  const diffMs = now.getTime() - date.getTime();
  const diffSecs = Math.floor(diffMs / 1000);
  const diffMins = Math.floor(diffSecs / 60);
  const diffHours = Math.floor(diffMins / 60);
  const diffDays = Math.floor(diffHours / 24);
  const diffWeeks = Math.floor(diffDays / 7);
  const diffMonths = Math.floor(diffDays / 30);

  if (diffSecs < 60) return 'just now';
  if (diffMins < 60) return `${diffMins}m ago`;
  if (diffHours < 24) return `${diffHours}h ago`;
  if (diffDays < 7) return `${diffDays}d ago`;
  if (diffWeeks < 5) return `${diffWeeks}w ago`;
  if (diffMonths < 12) return `${diffMonths}mo ago`;
  return date.toLocaleDateString();
}

function StashEntryItem({
  stash,
  onApply,
  onPop,
  onDrop,
  isApplying,
  isDropping,
}: {
  stash: StashEntry;
  onApply: (index: number) => void;
  onPop: (index: number) => void;
  onDrop: (index: number) => void;
  isApplying: boolean;
  isDropping: boolean;
}) {
  const [expanded, setExpanded] = useState(false);
  const isBusy = isApplying || isDropping;

  // Clean up the stash message for display
  const displayMessage =
    stash.message.replace(/^(WIP on|On) [^:]+:\s*[a-f0-9]+\s*/, '').trim() || stash.message;

  return (
    <div
      className={cn(
        'group relative rounded-md border bg-card transition-colors',
        'hover:border-primary/30'
      )}
    >
      {/* Header */}
      <div className="flex items-start gap-3 p-3">
        {/* Expand toggle & stash icon */}
        <button
          onClick={() => setExpanded(!expanded)}
          className="flex items-center gap-1 pt-0.5 text-muted-foreground hover:text-foreground transition-colors"
          disabled={stash.files.length === 0}
        >
          {stash.files.length > 0 ? (
            expanded ? (
              <ChevronDown className="w-3.5 h-3.5" />
            ) : (
              <ChevronRight className="w-3.5 h-3.5" />
            )
          ) : (
            <span className="w-3.5" />
          )}
          <Archive className="w-3.5 h-3.5" />
        </button>

        {/* Content */}
        <div className="flex-1 min-w-0">
          <div className="flex items-start justify-between gap-2">
            <div className="min-w-0">
              <p className="text-sm font-medium leading-snug break-words">{displayMessage}</p>
              <div className="flex flex-wrap items-center gap-x-2 gap-y-1 mt-1 text-xs text-muted-foreground">
                <span className="inline-flex items-center gap-1 font-mono bg-muted px-1.5 py-0.5 rounded text-[10px]">
                  stash@{'{' + stash.index + '}'}
                </span>
                {stash.branch && (
                  <span className="inline-flex items-center gap-1">
                    <GitBranch className="w-3 h-3" />
                    {stash.branch}
                  </span>
                )}
                <span className="inline-flex items-center gap-1">
                  <Clock className="w-3 h-3" />
                  <time
                    dateTime={stash.date}
                    title={
                      !isNaN(new Date(stash.date).getTime())
                        ? new Date(stash.date).toLocaleString()
                        : stash.date
                    }
                  >
                    {formatRelativeDate(stash.date)}
                  </time>
                </span>
                {stash.files.length > 0 && (
                  <span className="inline-flex items-center gap-1">
                    <FileText className="w-3 h-3" />
                    {stash.files.length} file{stash.files.length !== 1 ? 's' : ''}
                  </span>
                )}
              </div>
            </div>

            {/* Action buttons */}
            <div className="flex items-center gap-1 shrink-0">
              <Button
                size="sm"
                variant="outline"
                className="h-7 text-xs px-2"
                onClick={() => onApply(stash.index)}
                disabled={isBusy}
                title="Apply stash (keep in stash list)"
              >
                {isApplying ? <Spinner size="xs" /> : <Play className="w-3 h-3 mr-1" />}
                Apply
              </Button>
              <Button
                size="sm"
                variant="outline"
                className="h-7 text-xs px-2"
                onClick={() => onPop(stash.index)}
                disabled={isBusy}
                title="Pop stash (apply and remove from stash list)"
              >
                {isApplying ? <Spinner size="xs" /> : 'Pop'}
              </Button>
              <Button
                size="sm"
                variant="ghost"
                className="h-7 w-7 p-0 text-destructive hover:text-destructive"
                onClick={() => onDrop(stash.index)}
                disabled={isBusy}
                title="Delete this stash"
              >
                {isDropping ? <Spinner size="xs" /> : <Trash2 className="w-3 h-3" />}
              </Button>
            </div>
          </div>
        </div>
      </div>

      {/* Expanded file list */}
      {expanded && stash.files.length > 0 && (
        <div className="border-t px-3 py-2 bg-muted/30">
          <div className="space-y-0.5">
            {stash.files.map((file) => (
              <div
                key={file}
                className="flex items-center gap-2 text-xs text-muted-foreground py-0.5"
              >
                <FileText className="w-3 h-3 shrink-0" />
                <span className="font-mono break-all">{file}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

export function ViewStashesDialog({
  open,
  onOpenChange,
  worktree,
  onStashApplied,
}: ViewStashesDialogProps) {
  const [stashes, setStashes] = useState<StashEntry[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [applyingIndex, setApplyingIndex] = useState<number | null>(null);
  const [droppingIndex, setDroppingIndex] = useState<number | null>(null);

  const fetchStashes = useCallback(async () => {
    if (!worktree) return;

    setIsLoading(true);
    setError(null);

    try {
      const api = getHttpApiClient();
      const result = await api.worktree.stashList(worktree.path);

      if (result.success && result.result) {
        setStashes(result.result.stashes);
      } else {
        setError(result.error || 'Failed to load stashes');
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to load stashes');
    } finally {
      setIsLoading(false);
    }
  }, [worktree]);

  useEffect(() => {
    if (open && worktree) {
      fetchStashes();
    }
    if (!open) {
      setStashes([]);
      setError(null);
    }
  }, [open, worktree, fetchStashes]);

  const handleApply = async (stashIndex: number) => {
    if (!worktree) return;

    setApplyingIndex(stashIndex);
    try {
      const api = getHttpApiClient();
      const result = await api.worktree.stashApply(worktree.path, stashIndex, false);

      if (result.success && result.result) {
        if (result.result.hasConflicts) {
          toast.warning('Stash applied with conflicts', {
            description: 'Please resolve the merge conflicts.',
          });
        } else {
          toast.success('Stash applied');
        }
        onStashApplied?.();
      } else {
        toast.error('Failed to apply stash', {
          description: result.error || 'Unknown error',
        });
      }
    } catch (err) {
      toast.error('Failed to apply stash', {
        description: err instanceof Error ? err.message : 'Unknown error',
      });
    } finally {
      setApplyingIndex(null);
    }
  };

  const handlePop = async (stashIndex: number) => {
    if (!worktree) return;

    setApplyingIndex(stashIndex);
    try {
      const api = getHttpApiClient();
      const result = await api.worktree.stashApply(worktree.path, stashIndex, true);

      if (result.success && result.result) {
        if (result.result.hasConflicts) {
          toast.warning('Stash popped with conflicts', {
            description: 'Please resolve the merge conflicts. The stash was removed.',
          });
        } else {
          toast.success('Stash popped', {
            description: 'Changes applied and stash removed.',
          });
        }
        // Refresh the stash list since the stash was removed
        await fetchStashes();
        onStashApplied?.();
      } else {
        toast.error('Failed to pop stash', {
          description: result.error || 'Unknown error',
        });
      }
    } catch (err) {
      toast.error('Failed to pop stash', {
        description: err instanceof Error ? err.message : 'Unknown error',
      });
    } finally {
      setApplyingIndex(null);
    }
  };

  const handleDrop = async (stashIndex: number) => {
    if (!worktree) return;

    setDroppingIndex(stashIndex);
    try {
      const api = getHttpApiClient();
      const result = await api.worktree.stashDrop(worktree.path, stashIndex);

      if (result.success) {
        toast.success('Stash deleted');
        // Refresh the stash list
        await fetchStashes();
      } else {
        toast.error('Failed to delete stash', {
          description: result.error || 'Unknown error',
        });
      }
    } catch (err) {
      toast.error('Failed to delete stash', {
        description: err instanceof Error ? err.message : 'Unknown error',
      });
    } finally {
      setDroppingIndex(null);
    }
  };

  if (!worktree) return null;

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="w-full h-full max-w-full max-h-full sm:w-[90vw] sm:max-w-[640px] sm:max-h-[100dvh] sm:h-auto sm:rounded-xl rounded-none flex flex-col">
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2">
            <Archive className="w-5 h-5" />
            Stashes
          </DialogTitle>
          <DialogDescription>
            Stashed changes in{' '}
            <code className="font-mono bg-muted px-1 rounded">{worktree.branch}</code>
          </DialogDescription>
        </DialogHeader>

        <div className="flex-1 sm:min-h-[300px] sm:max-h-[60vh] overflow-y-auto scrollbar-visible -mx-6 -mb-6">
          <div className="h-full px-6 pb-6">
            {isLoading && (
              <div className="flex items-center justify-center py-12">
                <Spinner size="md" />
                <span className="ml-2 text-sm text-muted-foreground">Loading stashes...</span>
              </div>
            )}

            {error && (
              <div className="flex items-center justify-center py-12">
                <p className="text-sm text-destructive">{error}</p>
              </div>
            )}

            {!isLoading && !error && stashes.length === 0 && (
              <div className="flex flex-col items-center justify-center py-12 gap-2">
                <Archive className="w-8 h-8 text-muted-foreground/50" />
                <p className="text-sm text-muted-foreground">No stashes found</p>
                <p className="text-xs text-muted-foreground">
                  Use &quot;Stash Changes&quot; to save your uncommitted changes
                </p>
              </div>
            )}

            {!isLoading && !error && stashes.length > 0 && (
              <div className="space-y-2 mt-2">
                {stashes.map((stash) => (
                  <StashEntryItem
                    key={stash.index}
                    stash={stash}
                    onApply={handleApply}
                    onPop={handlePop}
                    onDrop={handleDrop}
                    isApplying={applyingIndex === stash.index}
                    isDropping={droppingIndex === stash.index}
                  />
                ))}
              </div>
            )}
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
}
