/**
 * Appointment Management Hooks
 */

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from '../api/client';
import { Appointment, AppointmentStatus } from '../types';
import { format } from 'date-fns';

interface AppointmentFilters {
  resource?: string;
  status?: AppointmentStatus;
  startDate?: Date;
  endDate?: Date;
}

/**
 * Hook to fetch appointments with optional filters
 */
export const useAppointments = (filters?: AppointmentFilters) => {
  return useQuery<Appointment[]>({
    queryKey: ['appointments', filters],
    queryFn: async () => {
      const params = new URLSearchParams();
      if (filters?.resource) params.append('resource', filters.resource);
      if (filters?.status) params.append('status', filters.status);
      // Send full ISO datetime strings to avoid timezone issues
      // The backend will compare datetime fields properly
      if (filters?.startDate) {
        // Start of day in local timezone, converted to ISO
        const startOfDay = new Date(filters.startDate);
        startOfDay.setHours(0, 0, 0, 0);
        params.append('start_date', startOfDay.toISOString());
      }
      if (filters?.endDate) {
        // End of day (or start of next day) in local timezone, converted to ISO
        const endOfDay = new Date(filters.endDate);
        endOfDay.setHours(0, 0, 0, 0);
        params.append('end_date', endOfDay.toISOString());
      }

      const { data } = await apiClient.get(`/api/appointments/?${params}`);

      // Transform backend format to frontend format
      return data.map((a: any) => ({
        id: String(a.id),
        resourceId: a.resource_id ? String(a.resource_id) : null,
        customerId: String(a.customer_id || a.customer),
        customerName: a.customer_name || '',
        serviceId: String(a.service_id || a.service),
        startTime: new Date(a.start_time),
        durationMinutes: a.duration_minutes || calculateDuration(a.start_time, a.end_time),
        status: a.status as AppointmentStatus,
        notes: a.notes || '',
      }));
    },
  });
};

/**
 * Calculate duration in minutes from start and end times
 */
function calculateDuration(startTime: string, endTime: string): number {
  const start = new Date(startTime);
  const end = new Date(endTime);
  return Math.round((end.getTime() - start.getTime()) / (1000 * 60));
}

/**
 * Hook to get a single appointment
 */
export const useAppointment = (id: string) => {
  return useQuery<Appointment>({
    queryKey: ['appointments', id],
    queryFn: async () => {
      const { data } = await apiClient.get(`/api/appointments/${id}/`);

      return {
        id: String(data.id),
        resourceId: data.resource_id ? String(data.resource_id) : null,
        customerId: String(data.customer_id || data.customer),
        customerName: data.customer_name || '',
        serviceId: String(data.service_id || data.service),
        startTime: new Date(data.start_time),
        durationMinutes: data.duration_minutes || calculateDuration(data.start_time, data.end_time),
        status: data.status as AppointmentStatus,
        notes: data.notes || '',
      };
    },
    enabled: !!id,
  });
};

/**
 * Hook to create an appointment
 */
export const useCreateAppointment = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (appointmentData: Omit<Appointment, 'id'>) => {
      const startTime = appointmentData.startTime;
      const endTime = new Date(startTime.getTime() + appointmentData.durationMinutes * 60000);

      const backendData: Record<string, unknown> = {
        service: parseInt(appointmentData.serviceId),
        resource: appointmentData.resourceId ? parseInt(appointmentData.resourceId) : null,
        start_time: startTime.toISOString(),
        end_time: endTime.toISOString(),
        notes: appointmentData.notes || '',
      };

      // Include customer if provided (for business-created appointments)
      if (appointmentData.customerId) {
        backendData.customer = parseInt(appointmentData.customerId);
      }

      const { data } = await apiClient.post('/api/appointments/', backendData);
      return data;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['appointments'] });
    },
  });
};

/**
 * Hook to update an appointment with optimistic updates for instant UI feedback
 */
export const useUpdateAppointment = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async ({ id, updates }: { id: string; updates: Partial<Appointment> }) => {
      const backendData: any = {};

      if (updates.serviceId) backendData.service = parseInt(updates.serviceId);
      if (updates.resourceId !== undefined) {
        backendData.resource = updates.resourceId ? parseInt(updates.resourceId) : null;
      }
      if (updates.startTime) {
        backendData.start_time = updates.startTime.toISOString();
        // Calculate end_time if we have duration, otherwise backend will keep existing duration
        if (updates.durationMinutes) {
          const endTime = new Date(updates.startTime.getTime() + updates.durationMinutes * 60000);
          backendData.end_time = endTime.toISOString();
        }
      } else if (updates.durationMinutes) {
        // If only duration changed, we need to get the current appointment to calculate new end time
        // For now, just send duration and let backend handle it
        // This case is handled by the resize logic which sends both startTime and durationMinutes
      }
      if (updates.status) backendData.status = updates.status;
      if (updates.notes !== undefined) backendData.notes = updates.notes;

      const { data } = await apiClient.patch(`/api/appointments/${id}/`, backendData);
      return data;
    },
    // Optimistic update: update UI immediately before API call completes
    onMutate: async ({ id, updates }) => {
      // Cancel any outgoing refetches so they don't overwrite our optimistic update
      await queryClient.cancelQueries({ queryKey: ['appointments'] });

      // Get all appointment queries and update them optimistically
      const queryCache = queryClient.getQueryCache();
      const appointmentQueries = queryCache.findAll({ queryKey: ['appointments'] });

      const previousData: { queryKey: unknown[]; data: Appointment[] | undefined }[] = [];

      appointmentQueries.forEach((query) => {
        const data = queryClient.getQueryData<Appointment[]>(query.queryKey);
        if (data) {
          previousData.push({ queryKey: query.queryKey, data });
          queryClient.setQueryData<Appointment[]>(query.queryKey, (old) => {
            if (!old) return old;
            return old.map((apt) =>
              apt.id === id ? { ...apt, ...updates } : apt
            );
          });
        }
      });

      // Return context with the previous values for rollback
      return { previousData };
    },
    // If mutation fails, rollback to the previous values
    onError: (error, _variables, context) => {
      console.error('Failed to update appointment', error);
      if (context?.previousData) {
        context.previousData.forEach(({ queryKey, data }) => {
          queryClient.setQueryData(queryKey, data);
        });
      }
    },
    // Always refetch after error or success to ensure server state
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['appointments'] });
    },
  });
};

/**
 * Hook to delete an appointment with optimistic updates
 */
export const useDeleteAppointment = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (id: string) => {
      await apiClient.delete(`/api/appointments/${id}/`);
      return id;
    },
    // Optimistic update: remove from UI immediately
    onMutate: async (id) => {
      await queryClient.cancelQueries({ queryKey: ['appointments'] });

      // Get all appointment queries and update them optimistically
      const queryCache = queryClient.getQueryCache();
      const appointmentQueries = queryCache.findAll({ queryKey: ['appointments'] });

      const previousData: { queryKey: unknown[]; data: Appointment[] | undefined }[] = [];

      appointmentQueries.forEach((query) => {
        const data = queryClient.getQueryData<Appointment[]>(query.queryKey);
        if (data) {
          previousData.push({ queryKey: query.queryKey, data });
          queryClient.setQueryData<Appointment[]>(query.queryKey, (old) => {
            if (!old) return old;
            return old.filter((apt) => apt.id !== id);
          });
        }
      });

      return { previousData };
    },
    onError: (error, _id, context) => {
      console.error('Failed to delete appointment', error);
      if (context?.previousData) {
        context.previousData.forEach(({ queryKey, data }) => {
          queryClient.setQueryData(queryKey, data);
        });
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['appointments'] });
    },
  });
};

/**
 * Hook to reschedule an appointment (update start time and resource)
 */
export const useRescheduleAppointment = () => {
  const updateMutation = useUpdateAppointment();

  return useMutation({
    mutationFn: async ({
      id,
      newStartTime,
      newResourceId,
    }: {
      id: string;
      newStartTime: Date;
      newResourceId?: string | null;
    }) => {
      const appointment = await apiClient.get(`/api/appointments/${id}/`);
      const durationMinutes = appointment.data.duration_minutes;

      return updateMutation.mutateAsync({
        id,
        updates: {
          startTime: newStartTime,
          durationMinutes,
          resourceId: newResourceId !== undefined ? newResourceId : undefined,
        },
      });
    },
  });
};
