/**
 * Conflict Resolver Component
 * 
 * This component handles conflicts that occur when syncing offline changes
 * with server data. It provides a UI for users to resolve conflicts by:
 * - Viewing local vs server versions side-by-side
 * - Choosing to keep local version
 * - Choosing to use server version
 * - Manually merging changes
 * 
 * Requirements: REQ-5.5.4
 */

'use client';

import React, { useState } from 'react';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
  AlertTriangle,
  CheckCircle2,
  XCircle,
  GitMerge,
  Clock,
  User,
  FileText
} from 'lucide-react';
import { cn } from '@/utils';

/**
 * Conflict data structure
 */
export interface ConflictData {
  /** Unique identifier for the conflict */
  id: string;
  
  /** Resource type that has a conflict */
  resourceType: 'canvas' | 'canvas-metadata' | 'asset' | 'storyboard';
  
  /** Resource ID */
  resourceId: string;
  
  /** Resource name for display */
  resourceName: string;
  
  /** Local version of the data */
  localVersion: any;
  
  /** Server version of the data */
  serverVersion: any;
  
  /** Timestamp of local version */
  localTimestamp: number;
  
  /** Timestamp of server version */
  serverTimestamp: number;
  
  /** Fields that have conflicts */
  conflictingFields?: string[];
}

/**
 * Resolution choice
 */
export type ResolutionChoice = 'local' | 'server' | 'merged';

/**
 * Conflict resolver props
 */
export interface ConflictResolverProps {
  /** Conflict data to resolve */
  conflict: ConflictData | null;
  
  /** Whether the dialog is open */
  open: boolean;
  
  /** Callback when dialog should close */
  onClose: () => void;
  
  /** Callback when conflict is resolved */
  onResolve: (conflictId: string, choice: ResolutionChoice, mergedData?: any) => void;
  
  /** Additional CSS classes */
  className?: string;
}

/**
 * Format timestamp for display
 */
function formatTimestamp(timestamp: number): string {
  const date = new Date(timestamp);
  const now = new Date();
  const diffMs = now.getTime() - date.getTime();
  const diffMins = Math.floor(diffMs / 60000);
  const diffHours = Math.floor(diffMs / 3600000);
  const diffDays = Math.floor(diffMs / 86400000);

  if (diffMins < 1) return 'Just now';
  if (diffMins < 60) return `${diffMins} minute${diffMins !== 1 ? 's' : ''} ago`;
  if (diffHours < 24) return `${diffHours} hour${diffHours !== 1 ? 's' : ''} ago`;
  if (diffDays < 7) return `${diffDays} day${diffDays !== 1 ? 's' : ''} ago`;
  
  return date.toLocaleDateString();
}

/**
 * Render data comparison
 */
function DataComparison({ 
  label, 
  localData, 
  serverData, 
  conflictingFields 
}: { 
  label: string;
  localData: any;
  serverData: any;
  conflictingFields?: string[];
}) {
  const renderValue = (value: any): string => {
    if (value === null || value === undefined) return 'N/A';
    if (typeof value === 'object') return JSON.stringify(value, null, 2);
    return String(value);
  };

  const isConflicting = (field: string) => {
    return conflictingFields?.includes(field) ?? false;
  };

  // Get all unique keys from both versions
  const allKeys = new Set([
    ...Object.keys(localData || {}),
    ...Object.keys(serverData || {})
  ]);

  return (
    <div className="space-y-2">
      <h4 className="text-sm font-medium text-muted-foreground">{label}</h4>
      <div className="grid grid-cols-2 gap-4">
        {/* Local Version */}
        <div className="space-y-2">
          <div className="flex items-center gap-2 text-sm font-medium">
            <User className="h-4 w-4" />
            <span>Your Version</span>
          </div>
          <ScrollArea className="h-[300px] rounded-md border bg-muted/50 p-3">
            <div className="space-y-2 text-xs font-mono">
              {Array.from(allKeys).map((key) => (
                <div key={key} className={cn(
                  'rounded p-1',
                  isConflicting(key) && 'bg-orange-100 dark:bg-orange-950/30'
                )}>
                  <div className="font-semibold text-foreground">{key}:</div>
                  <div className="text-muted-foreground whitespace-pre-wrap break-all">
                    {renderValue(localData?.[key])}
                  </div>
                </div>
              ))}
            </div>
          </ScrollArea>
        </div>

        {/* Server Version */}
        <div className="space-y-2">
          <div className="flex items-center gap-2 text-sm font-medium">
            <FileText className="h-4 w-4" />
            <span>Server Version</span>
          </div>
          <ScrollArea className="h-[300px] rounded-md border bg-muted/50 p-3">
            <div className="space-y-2 text-xs font-mono">
              {Array.from(allKeys).map((key) => (
                <div key={key} className={cn(
                  'rounded p-1',
                  isConflicting(key) && 'bg-orange-100 dark:bg-orange-950/30'
                )}>
                  <div className="font-semibold text-foreground">{key}:</div>
                  <div className="text-muted-foreground whitespace-pre-wrap break-all">
                    {renderValue(serverData?.[key])}
                  </div>
                </div>
              ))}
            </div>
          </ScrollArea>
        </div>
      </div>
    </div>
  );
}

/**
 * Conflict Resolver Component
 * 
 * Displays a dialog for resolving sync conflicts between local and server data.
 * 
 * @example
 * ```tsx
 * const [conflict, setConflict] = useState<ConflictData | null>(null);
 * 
 * <ConflictResolver
 *   conflict={conflict}
 *   open={!!conflict}
 *   onClose={() => setConflict(null)}
 *   onResolve={(id, choice, mergedData) => {
 *     // Handle resolution
 *     console.log(`Resolved ${id} with choice: ${choice}`);
 *     setConflict(null);
 *   }}
 * />
 * ```
 */
export function ConflictResolver({
  conflict,
  open,
  onClose,
  onResolve,
  className
}: ConflictResolverProps) {
  const [selectedChoice, setSelectedChoice] = useState<ResolutionChoice | null>(null);
  const [mergedData, setMergedData] = useState<any>(null);
  const [fieldSelections, setFieldSelections] = useState<Map<string, 'local' | 'server'>>(new Map());

  React.useEffect(() => {
    if (conflict) {
      setFieldSelections(new Map());
      setSelectedChoice(null);
      setMergedData(null);
    }
  }, [conflict]);

  if (!conflict) return null;

  // Build merged data from field selections
  const buildMergedData = () => {
    const merged: any = {};
    const allKeys = new Set([
      ...Object.keys(conflict.localVersion || {}),
      ...Object.keys(conflict.serverVersion || {})
    ]);

    allKeys.forEach((key) => {
      const selection = fieldSelections.get(key);
      if (selection === 'local') {
        merged[key] = conflict.localVersion?.[key];
      } else if (selection === 'server') {
        merged[key] = conflict.serverVersion?.[key];
      } else {
        // Default to local if no selection
        merged[key] = conflict.localVersion?.[key];
      }
    });

    return merged;
  };

  const handleResolve = () => {
    if (!selectedChoice) return;
    
    let dataToResolve = mergedData;
    
    // If manual merge, build the merged data from field selections
    if (selectedChoice === 'merged') {
      dataToResolve = buildMergedData();
    }
    
    onResolve(conflict.id, selectedChoice, dataToResolve);
    setSelectedChoice(null);
    setMergedData(null);
    setFieldSelections(new Map());
  };

  const handleCancel = () => {
    setSelectedChoice(null);
    setMergedData(null);
    setFieldSelections(new Map());
    onClose();
  };

  const handleFieldSelection = (field: string, version: 'local' | 'server') => {
    setFieldSelections((prev) => {
      const newSelections = new Map(prev);
      newSelections.set(field, version);
      return newSelections;
    });
  };

  const getFieldSelection = (field: string): 'local' | 'server' | null => {
    return fieldSelections.get(field) || null;
  };

  // Check if all fields have been selected for manual merge
  const allFieldsSelected = () => {
    if (selectedChoice !== 'merged') return true;
    
    const allKeys = new Set([
      ...Object.keys(conflict.localVersion || {}),
      ...Object.keys(conflict.serverVersion || {})
    ]);

    return Array.from(allKeys).every((key) => fieldSelections.has(key));
  };

  return (
    <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleCancel()}>
      <DialogContent className={cn('max-w-4xl max-h-[90vh]', className)}>
        <DialogHeader>
          <div className="flex items-center gap-2">
            <AlertTriangle className="h-5 w-5 text-orange-500" />
            <DialogTitle>Sync Conflict Detected</DialogTitle>
          </div>
          <DialogDescription>
            Changes were made to <strong>{conflict.resourceName}</strong> both locally and on the server.
            Please choose how to resolve this conflict.
          </DialogDescription>
        </DialogHeader>

        <div className="space-y-4">
          {/* Conflict Info */}
          <div className="flex items-center gap-4 rounded-lg border bg-muted/50 p-3">
            <div className="flex-1">
              <div className="flex items-center gap-2 text-sm">
                <Badge variant="outline">{conflict.resourceType}</Badge>
                <span className="font-medium">{conflict.resourceName}</span>
              </div>
            </div>
            <div className="flex items-center gap-4 text-xs text-muted-foreground">
              <div className="flex items-center gap-1">
                <Clock className="h-3 w-3" />
                <span>Local: {formatTimestamp(conflict.localTimestamp)}</span>
              </div>
              <div className="flex items-center gap-1">
                <Clock className="h-3 w-3" />
                <span>Server: {formatTimestamp(conflict.serverTimestamp)}</span>
              </div>
            </div>
          </div>

          {/* Conflicting Fields */}
          {conflict.conflictingFields && conflict.conflictingFields.length > 0 && (
            <div className="rounded-lg border bg-orange-50 dark:bg-orange-950/20 p-3">
              <p className="text-sm font-medium text-orange-900 dark:text-orange-100">
                Conflicting fields:
              </p>
              <div className="mt-1 flex flex-wrap gap-1">
                {conflict.conflictingFields.map((field) => (
                  <Badge key={field} variant="secondary" className="text-xs">
                    {field}
                  </Badge>
                ))}
              </div>
            </div>
          )}

          {/* Resolution Options */}
          <Tabs 
            defaultValue="compare" 
            className="w-full"
            onValueChange={(value) => {
              if (value === 'merged') {
                setSelectedChoice('merged');
              } else {
                // Reset merge selection when switching away
                if (selectedChoice === 'merged') {
                  setSelectedChoice(null);
                  setFieldSelections(new Map());
                }
              }
            }}
          >
            <TabsList className="grid w-full grid-cols-2">
              <TabsTrigger value="compare">Compare Versions</TabsTrigger>
              <TabsTrigger value="merge">
                Manual Merge
              </TabsTrigger>
            </TabsList>

            <TabsContent value="compare" className="space-y-4">
              <DataComparison
                label="Data Comparison"
                localData={conflict.localVersion}
                serverData={conflict.serverVersion}
                conflictingFields={conflict.conflictingFields}
              />

              {/* Choice Buttons */}
              <div className="grid grid-cols-2 gap-3">
                <Button
                  variant={selectedChoice === 'local' ? 'default' : 'outline'}
                  className="h-auto flex-col items-start gap-2 p-4"
                  onClick={() => setSelectedChoice('local')}
                >
                  <div className="flex items-center gap-2">
                    <CheckCircle2 className="h-4 w-4" />
                    <span className="font-semibold">Keep My Version</span>
                  </div>
                  <p className="text-xs text-left text-muted-foreground">
                    Discard server changes and keep your local changes
                  </p>
                </Button>

                <Button
                  variant={selectedChoice === 'server' ? 'default' : 'outline'}
                  className="h-auto flex-col items-start gap-2 p-4"
                  onClick={() => setSelectedChoice('server')}
                >
                  <div className="flex items-center gap-2">
                    <XCircle className="h-4 w-4" />
                    <span className="font-semibold">Use Server Version</span>
                  </div>
                  <p className="text-xs text-left text-muted-foreground">
                    Discard your local changes and use the server version
                  </p>
                </Button>
              </div>
            </TabsContent>

            <TabsContent value="merge" className="space-y-4">
              <div className="space-y-3">
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <GitMerge className="h-4 w-4" />
                  <span>Select which version to keep for each field</span>
                </div>

                <ScrollArea className="h-[400px] rounded-md border">
                  <div className="p-4 space-y-3">
                    {(() => {
                      const allKeys = new Set([
                        ...Object.keys(conflict.localVersion || {}),
                        ...Object.keys(conflict.serverVersion || {})
                      ]);

                      return Array.from(allKeys).map((key) => {
                        const localValue = conflict.localVersion?.[key];
                        const serverValue = conflict.serverVersion?.[key];
                        const isConflicting = conflict.conflictingFields?.includes(key);
                        const selection = getFieldSelection(key);

                        const renderValue = (value: any): string => {
                          if (value === null || value === undefined) return 'N/A';
                          if (typeof value === 'object') return JSON.stringify(value, null, 2);
                          return String(value);
                        };

                        return (
                          <div
                            key={key}
                            className={cn(
                              'rounded-lg border p-3 space-y-2',
                              isConflicting && 'border-orange-300 bg-orange-50 dark:bg-orange-950/20'
                            )}
                          >
                            <div className="flex items-center justify-between">
                              <div className="flex items-center gap-2">
                                <span className="font-medium text-sm">{key}</span>
                                {isConflicting && (
                                  <Badge variant="destructive" className="text-xs">
                                    Conflict
                                  </Badge>
                                )}
                              </div>
                              {selection && (
                                <Badge variant="secondary" className="text-xs">
                                  {selection === 'local' ? 'Using Local' : 'Using Server'}
                                </Badge>
                              )}
                            </div>

                            <div className="grid grid-cols-2 gap-2">
                              {/* Local Version */}
                              <Button
                                variant={selection === 'local' ? 'default' : 'outline'}
                                className="h-auto flex-col items-start gap-1 p-3 text-left"
                                onClick={() => handleFieldSelection(key, 'local')}
                              >
                                <div className="flex items-center gap-1 text-xs font-medium">
                                  <User className="h-3 w-3" />
                                  <span>Your Version</span>
                                </div>
                                <div className="text-xs font-mono text-muted-foreground whitespace-pre-wrap break-all max-h-20 overflow-hidden">
                                  {renderValue(localValue)}
                                </div>
                              </Button>

                              {/* Server Version */}
                              <Button
                                variant={selection === 'server' ? 'default' : 'outline'}
                                className="h-auto flex-col items-start gap-1 p-3 text-left"
                                onClick={() => handleFieldSelection(key, 'server')}
                              >
                                <div className="flex items-center gap-1 text-xs font-medium">
                                  <FileText className="h-3 w-3" />
                                  <span>Server Version</span>
                                </div>
                                <div className="text-xs font-mono text-muted-foreground whitespace-pre-wrap break-all max-h-20 overflow-hidden">
                                  {renderValue(serverValue)}
                                </div>
                              </Button>
                            </div>
                          </div>
                        );
                      });
                    })()}
                  </div>
                </ScrollArea>

                {!allFieldsSelected() && (
                  <div className="rounded-lg border border-orange-300 bg-orange-50 dark:bg-orange-950/20 p-3">
                    <p className="text-sm text-orange-900 dark:text-orange-100">
                      Please select a version for all fields before resolving.
                    </p>
                  </div>
                )}
              </div>
            </TabsContent>
          </Tabs>
        </div>

        <DialogFooter>
          <Button variant="outline" onClick={handleCancel}>
            Cancel
          </Button>
          <Button
            onClick={handleResolve}
            disabled={!selectedChoice || (selectedChoice === 'merged' && !allFieldsSelected())}
          >
            Resolve Conflict
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

/**
 * Hook for managing conflict resolution
 * 
 * Provides state management for conflict resolution workflow.
 * 
 * @example
 * ```tsx
 * const { 
 *   conflict, 
 *   showConflict, 
 *   resolveConflict, 
 *   dismissConflict 
 * } = useConflictResolver();
 * 
 * // When a conflict is detected
 * showConflict({
 *   id: 'conflict-1',
 *   resourceType: 'canvas',
 *   resourceId: 'canvas-123',
 *   resourceName: 'My Canvas',
 *   localVersion: localData,
 *   serverVersion: serverData,
 *   localTimestamp: Date.now(),
 *   serverTimestamp: Date.now() - 1000
 * });
 * 
 * // Render the resolver
 * <ConflictResolver
 *   conflict={conflict}
 *   open={!!conflict}
 *   onClose={dismissConflict}
 *   onResolve={resolveConflict}
 * />
 * ```
 */
export function useConflictResolver() {
  const [conflict, setConflict] = useState<ConflictData | null>(null);
  const [resolvedConflicts, setResolvedConflicts] = useState<Map<string, ResolutionChoice>>(new Map());

  const showConflict = (conflictData: ConflictData) => {
    setConflict(conflictData);
  };

  const dismissConflict = () => {
    setConflict(null);
  };

  const resolveConflict = (
    conflictId: string,
    choice: ResolutionChoice,
    mergedData?: any
  ) => {
    // Store the resolution
    setResolvedConflicts((prev) => new Map(prev).set(conflictId, choice));
    
    // Clear current conflict
    setConflict(null);
    
    // Return the resolution for the caller to handle
    return { choice, mergedData };
  };

  const getResolution = (conflictId: string): ResolutionChoice | undefined => {
    return resolvedConflicts.get(conflictId);
  };

  const clearResolutions = () => {
    setResolvedConflicts(new Map());
  };

  return {
    conflict,
    showConflict,
    dismissConflict,
    resolveConflict,
    getResolution,
    clearResolutions
  };
}
