/**
 * DraggableJobBlock Component
 *
 * Allows drag-and-drop rescheduling and resize of appointments on the timeline.
 * Features:
 * - Drag to move: Changes start/end time while preserving duration
 * - Drag edges to resize: Changes duration
 * - 15-minute snap: All changes snap to 15-minute intervals
 * - Permission check: Only allows editing if user has permission
 */

import React, { useCallback, useMemo } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Alert,
  Dimensions,
} from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  runOnJS,
} from 'react-native-reanimated';
import { getJobColor } from '../api/jobs';
import type { JobListItem } from '../types';

const HOUR_HEIGHT = 60;
const SNAP_MINUTES = 15;
const RESIZE_HANDLE_HEIGHT = 12;
const SCREEN_WIDTH = Dimensions.get('window').width;
const DAY_VIEW_WIDTH = SCREEN_WIDTH - 70;
const DAY_COLUMN_WIDTH = (SCREEN_WIDTH - 50) / 7;

// Convert pixels to minutes
function pixelsToMinutes(pixels: number): number {
  return (pixels / HOUR_HEIGHT) * 60;
}

// Convert minutes to pixels
function minutesToPixels(minutes: number): number {
  return (minutes / 60) * HOUR_HEIGHT;
}

// Snap to nearest 15 minutes
function snapToInterval(minutes: number): number {
  return Math.round(minutes / SNAP_MINUTES) * SNAP_MINUTES;
}

// Format time for display
function formatTime(dateString: string): string {
  const date = new Date(dateString);
  return date.toLocaleTimeString('en-US', {
    hour: 'numeric',
    minute: '2-digit',
    hour12: true,
  });
}

interface DraggableJobBlockProps {
  job: JobListItem;
  onPress: () => void;
  onTimeChange: (jobId: number, newStartTime: Date, newEndTime: Date) => Promise<void>;
  canEdit: boolean;
  viewMode: 'day' | 'week';
  dayIndex?: number;
  laneIndex?: number;
  totalLanes?: number;
}

export function DraggableJobBlock({
  job,
  onPress,
  onTimeChange,
  canEdit,
  viewMode,
  dayIndex = 0,
  laneIndex = 0,
  totalLanes = 1,
}: DraggableJobBlockProps) {
  const statusColor = getJobColor(job);

  const startDate = new Date(job.start_time);
  const endDate = new Date(job.end_time);

  const startHour = startDate.getHours() + startDate.getMinutes() / 60;
  const endHour = endDate.getHours() + endDate.getMinutes() / 60;

  const initialTop = startHour * HOUR_HEIGHT;
  const initialHeight = Math.max((endHour - startHour) * HOUR_HEIGHT, 40);

  // Shared values for animations
  const translateY = useSharedValue(0);
  const blockHeight = useSharedValue(initialHeight);
  const isActive = useSharedValue(false);
  const resizeMode = useSharedValue<'none' | 'top' | 'bottom'>('none');

  // Calculate width and position based on lanes
  const blockStyle = useMemo(() => {
    if (viewMode === 'week') {
      const laneWidth = (DAY_COLUMN_WIDTH - 4) / totalLanes;
      return {
        left: dayIndex * DAY_COLUMN_WIDTH + laneIndex * laneWidth,
        width: laneWidth - 2,
      };
    } else {
      const laneWidth = DAY_VIEW_WIDTH / totalLanes;
      return {
        left: laneIndex * laneWidth,
        width: laneWidth - 4,
      };
    }
  }, [viewMode, dayIndex, laneIndex, totalLanes]);

  // Calculate new times from current position
  const calculateNewTimes = useCallback((
    deltaY: number,
    heightDelta: number,
    mode: 'none' | 'top' | 'bottom'
  ): { newStart: Date; newEnd: Date } => {
    const deltaMinutes = pixelsToMinutes(deltaY);
    const heightDeltaMinutes = pixelsToMinutes(heightDelta);

    let newStartMinutes = startDate.getHours() * 60 + startDate.getMinutes();
    let newEndMinutes = endDate.getHours() * 60 + endDate.getMinutes();

    if (mode === 'none') {
      // Moving the whole block
      const snappedDelta = snapToInterval(deltaMinutes);
      newStartMinutes += snappedDelta;
      newEndMinutes += snappedDelta;
    } else if (mode === 'top') {
      // Resizing from top
      const snappedDelta = snapToInterval(deltaMinutes);
      newStartMinutes += snappedDelta;
      // Minimum 15 minutes duration
      if (newEndMinutes - newStartMinutes < SNAP_MINUTES) {
        newStartMinutes = newEndMinutes - SNAP_MINUTES;
      }
    } else if (mode === 'bottom') {
      // Resizing from bottom
      const snappedDelta = snapToInterval(heightDeltaMinutes);
      newEndMinutes = (startDate.getHours() * 60 + startDate.getMinutes()) +
        snapToInterval((endHour - startHour) * 60 + heightDeltaMinutes);
      // Minimum 15 minutes duration
      if (newEndMinutes - newStartMinutes < SNAP_MINUTES) {
        newEndMinutes = newStartMinutes + SNAP_MINUTES;
      }
    }

    // Clamp to valid day range (0:00 - 23:59)
    newStartMinutes = Math.max(0, Math.min(newStartMinutes, 24 * 60 - SNAP_MINUTES));
    newEndMinutes = Math.max(SNAP_MINUTES, Math.min(newEndMinutes, 24 * 60));

    const newStart = new Date(startDate);
    newStart.setHours(Math.floor(newStartMinutes / 60), newStartMinutes % 60, 0, 0);

    const newEnd = new Date(endDate);
    newEnd.setHours(Math.floor(newEndMinutes / 60), newEndMinutes % 60, 0, 0);

    return { newStart, newEnd };
  }, [startDate, endDate, startHour, endHour]);

  // Handle the end of a gesture
  const handleGestureEnd = useCallback(async (
    deltaY: number,
    heightDelta: number,
    mode: 'none' | 'top' | 'bottom'
  ) => {
    if (!canEdit) return;

    const { newStart, newEnd } = calculateNewTimes(deltaY, heightDelta, mode);

    // Check if times actually changed
    if (newStart.getTime() === startDate.getTime() && newEnd.getTime() === endDate.getTime()) {
      return;
    }

    try {
      await onTimeChange(job.id, newStart, newEnd);
    } catch (error: any) {
      Alert.alert('Error', error.message || 'Failed to update appointment time');
    }
  }, [canEdit, calculateNewTimes, startDate, endDate, job.id, onTimeChange]);

  // Main drag gesture (move the whole block)
  const dragGesture = Gesture.Pan()
    .enabled(canEdit)
    .onStart(() => {
      isActive.value = true;
      resizeMode.value = 'none';
    })
    .onUpdate((event) => {
      if (resizeMode.value === 'none') {
        // Snap to 15-minute intervals while dragging
        const snappedY = minutesToPixels(snapToInterval(pixelsToMinutes(event.translationY)));
        translateY.value = snappedY;
      }
    })
    .onEnd((event) => {
      isActive.value = false;
      const finalY = translateY.value;
      translateY.value = withSpring(0, { damping: 20 });
      runOnJS(handleGestureEnd)(finalY, 0, 'none');
    });

  // Top resize gesture
  const topResizeGesture = Gesture.Pan()
    .enabled(canEdit)
    .onStart(() => {
      isActive.value = true;
      resizeMode.value = 'top';
    })
    .onUpdate((event) => {
      const snappedY = minutesToPixels(snapToInterval(pixelsToMinutes(event.translationY)));
      translateY.value = snappedY;
      // Height decreases as top moves down
      const newHeight = Math.max(initialHeight - snappedY, minutesToPixels(SNAP_MINUTES));
      blockHeight.value = newHeight;
    })
    .onEnd((event) => {
      isActive.value = false;
      const finalY = translateY.value;
      translateY.value = withSpring(0, { damping: 20 });
      blockHeight.value = withSpring(initialHeight, { damping: 20 });
      runOnJS(handleGestureEnd)(finalY, 0, 'top');
    });

  // Bottom resize gesture
  const bottomResizeGesture = Gesture.Pan()
    .enabled(canEdit)
    .onStart(() => {
      isActive.value = true;
      resizeMode.value = 'bottom';
    })
    .onUpdate((event) => {
      const snappedDelta = minutesToPixels(snapToInterval(pixelsToMinutes(event.translationY)));
      const newHeight = Math.max(initialHeight + snappedDelta, minutesToPixels(SNAP_MINUTES));
      blockHeight.value = newHeight;
    })
    .onEnd((event) => {
      isActive.value = false;
      const heightDelta = blockHeight.value - initialHeight;
      blockHeight.value = withSpring(initialHeight, { damping: 20 });
      runOnJS(handleGestureEnd)(0, heightDelta, 'bottom');
    });

  // Tap gesture for navigation
  const tapGesture = Gesture.Tap()
    .onEnd(() => {
      runOnJS(onPress)();
    });

  // Combine gestures
  const composedGesture = Gesture.Race(
    tapGesture,
    dragGesture
  );

  // Animated styles
  const animatedBlockStyle = useAnimatedStyle(() => ({
    transform: [{ translateY: translateY.value }],
    height: blockHeight.value,
    opacity: isActive.value ? 0.8 : 1,
    zIndex: isActive.value ? 100 : 1,
  }));

  return (
    <GestureDetector gesture={composedGesture}>
      <Animated.View
        style={[
          styles.jobBlock,
          {
            top: initialTop,
            borderLeftColor: statusColor,
            ...blockStyle,
          },
          animatedBlockStyle,
        ]}
      >
        {/* Top resize handle */}
        {canEdit && (
          <GestureDetector gesture={topResizeGesture}>
            <View style={styles.resizeHandleTop}>
              <View style={styles.resizeBar} />
            </View>
          </GestureDetector>
        )}

        {/* Content */}
        <View style={styles.content}>
          <View style={styles.jobBlockHeader}>
            <Text style={[styles.jobBlockTime, viewMode === 'week' && styles.jobBlockTimeSmall]}>
              {formatTime(job.start_time)}
            </Text>
            <View style={[styles.statusDot, { backgroundColor: statusColor }]} />
          </View>
          <Text
            style={[styles.jobBlockTitle, viewMode === 'week' && styles.jobBlockTitleSmall]}
            numberOfLines={viewMode === 'week' ? 1 : 2}
          >
            {job.title}
          </Text>
          {viewMode === 'day' && job.customer_name && (
            <Text style={styles.jobBlockCustomer} numberOfLines={1}>
              {job.customer_name}
            </Text>
          )}
          {viewMode === 'day' && job.address && (
            <Text style={styles.jobBlockAddress} numberOfLines={1}>
              {job.address}
            </Text>
          )}
        </View>

        {/* Bottom resize handle */}
        {canEdit && (
          <GestureDetector gesture={bottomResizeGesture}>
            <View style={styles.resizeHandleBottom}>
              <View style={styles.resizeBar} />
            </View>
          </GestureDetector>
        )}
      </Animated.View>
    </GestureDetector>
  );
}

const styles = StyleSheet.create({
  jobBlock: {
    position: 'absolute',
    backgroundColor: '#fff',
    borderRadius: 6,
    borderLeftWidth: 3,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
    elevation: 2,
    overflow: 'visible',
  },
  content: {
    flex: 1,
    padding: 6,
    paddingTop: RESIZE_HANDLE_HEIGHT / 2,
    paddingBottom: RESIZE_HANDLE_HEIGHT / 2,
  },
  resizeHandleTop: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    height: RESIZE_HANDLE_HEIGHT,
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 10,
  },
  resizeHandleBottom: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    height: RESIZE_HANDLE_HEIGHT,
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 10,
  },
  resizeBar: {
    width: 30,
    height: 3,
    backgroundColor: '#d1d5db',
    borderRadius: 2,
  },
  jobBlockHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 2,
  },
  jobBlockTime: {
    fontSize: 10,
    color: '#6b7280',
    fontWeight: '500',
  },
  jobBlockTimeSmall: {
    fontSize: 9,
  },
  statusDot: {
    width: 6,
    height: 6,
    borderRadius: 3,
  },
  jobBlockTitle: {
    fontSize: 13,
    fontWeight: '600',
    color: '#111827',
    marginBottom: 2,
  },
  jobBlockTitleSmall: {
    fontSize: 10,
  },
  jobBlockCustomer: {
    fontSize: 11,
    color: '#4b5563',
  },
  jobBlockAddress: {
    fontSize: 10,
    color: '#9ca3af',
    marginTop: 2,
  },
});

export default DraggableJobBlock;
