import { useState, FormEvent } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import api, { getErrorMessage } from '@/lib/api';
import type { CalendarEvent, Location, RecurrenceRule } from '@/types';
import { useCalendars } from '@/hooks/useCalendars';
import {
  Sheet,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetFooter,
  SheetClose,
} from '@/components/ui/sheet';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Select } from '@/components/ui/select';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import LocationPicker from '@/components/ui/location-picker';

interface EventFormProps {
  event: CalendarEvent | null;
  templateData?: Partial<CalendarEvent> | null;
  templateName?: string | null;
  initialStart?: string | null;
  initialEnd?: string | null;
  initialAllDay?: boolean;
  editScope?: 'this' | 'this_and_future' | null;
  onClose: () => void;
}

function toDateOnly(dt: string): string {
  if (!dt) return '';
  return dt.split('T')[0];
}

function toDatetimeLocal(dt: string, fallbackTime: string = '09:00'): string {
  if (!dt) return '';
  if (dt.includes('T')) return dt.slice(0, 16);
  return `${dt}T${fallbackTime}`;
}

function formatForInput(dt: string, allDay: boolean, fallbackTime: string = '09:00'): string {
  if (!dt) return '';
  return allDay ? toDateOnly(dt) : toDatetimeLocal(dt, fallbackTime);
}

/** FullCalendar uses exclusive end dates for all-day events. Subtract 1 day for display. */
function adjustAllDayEndForDisplay(dateStr: string): string {
  if (!dateStr) return '';
  const d = new Date(dateStr.split('T')[0] + 'T12:00:00');
  d.setDate(d.getDate() - 1);
  const pad = (n: number) => n.toString().padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}

/** Add 1 day to form end date before sending to API for all-day events. */
function adjustAllDayEndForSave(dateStr: string): string {
  if (!dateStr) return '';
  const d = new Date(dateStr + 'T12:00:00');
  d.setDate(d.getDate() + 1);
  const pad = (n: number) => n.toString().padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}

// Python weekday: 0=Monday, 6=Sunday
const WEEKDAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];

function parseRecurrenceRule(raw?: string): RecurrenceRule | null {
  if (!raw) return null;
  try {
    return JSON.parse(raw);
  } catch {
    return null;
  }
}

function nowLocal(): string {
  const now = new Date();
  const pad = (n: number) => n.toString().padStart(2, '0');
  return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
}

function plusOneHour(dt: string): string {
  const d = new Date(dt);
  d.setHours(d.getHours() + 1);
  const pad = (n: number) => n.toString().padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

export default function EventForm({ event, templateData, templateName, initialStart, initialEnd, initialAllDay, editScope, onClose }: EventFormProps) {
  const queryClient = useQueryClient();
  const { data: calendars = [] } = useCalendars();
  // Merge template data as defaults for new events
  const source = event || templateData;
  const isAllDay = source?.all_day ?? initialAllDay ?? false;

  // Default to current time / +1 hour when creating a new event with no selection
  const defaultStart = nowLocal();
  const defaultEnd = plusOneHour(defaultStart);
  const rawStart = event?.start_datetime || initialStart || defaultStart;
  const rawEnd = event?.end_datetime || initialEnd || defaultEnd;

  const defaultCalendar = calendars.find((c) => c.is_default);
  const initialCalendarId = source?.calendar_id?.toString() || defaultCalendar?.id?.toString() || '';
  const isEditing = !!event?.id;

  // For all-day events, adjust end date for display (FullCalendar exclusive end)
  const displayEnd = isAllDay ? adjustAllDayEndForDisplay(rawEnd) : rawEnd;

  const [formData, setFormData] = useState({
    title: source?.title || '',
    description: source?.description || '',
    start_datetime: formatForInput(rawStart, isAllDay, '09:00'),
    end_datetime: formatForInput(displayEnd, isAllDay, '10:00'),
    all_day: isAllDay,
    location_id: source?.location_id?.toString() || '',
    calendar_id: initialCalendarId,
    is_starred: source?.is_starred || false,
  });

  const existingRule = parseRecurrenceRule(source?.recurrence_rule);
  const [recurrenceType, setRecurrenceType] = useState<string>(existingRule?.type || '');
  const [recurrenceInterval, setRecurrenceInterval] = useState(existingRule?.interval || 2);
  const [recurrenceWeekday, setRecurrenceWeekday] = useState(existingRule?.weekday ?? 1);
  const [recurrenceWeek, setRecurrenceWeek] = useState(existingRule?.week || 1);
  const [recurrenceDay, setRecurrenceDay] = useState(existingRule?.day || 1);

  const { data: locations = [] } = useQuery({
    queryKey: ['locations'],
    queryFn: async () => {
      const { data } = await api.get<Location[]>('/locations');
      return data;
    },
  });

  // Location picker state
  const existingLocation = locations.find((l) => l.id === source?.location_id);
  const [locationSearch, setLocationSearch] = useState(existingLocation?.name || '');

  const selectableCalendars = calendars.filter((c) => !c.is_system);

  const buildRecurrenceRule = (): RecurrenceRule | null => {
    if (!recurrenceType) return null;
    switch (recurrenceType) {
      case 'every_n_days':
        return { type: 'every_n_days', interval: recurrenceInterval };
      case 'weekly':
        // No weekday needed — backend derives it from the event's start date
        return { type: 'weekly' };
      case 'monthly_nth_weekday':
        return { type: 'monthly_nth_weekday', week: recurrenceWeek, weekday: recurrenceWeekday };
      case 'monthly_date':
        return { type: 'monthly_date', day: recurrenceDay };
      default:
        return null;
    }
  };

  const mutation = useMutation({
    mutationFn: async (data: typeof formData) => {
      const rule = buildRecurrenceRule();

      // Adjust end date for all-day events before save
      let endDt = data.end_datetime;
      if (data.all_day && endDt) {
        endDt = adjustAllDayEndForSave(endDt);
      }

      const payload: Record<string, any> = {
        title: data.title,
        description: data.description,
        start_datetime: data.start_datetime,
        end_datetime: endDt,
        all_day: data.all_day,
        location_id: data.location_id ? parseInt(data.location_id) : null,
        calendar_id: data.calendar_id ? parseInt(data.calendar_id) : null,
        is_starred: data.is_starred,
        recurrence_rule: rule,
      };

      if (isEditing) {
        if (editScope) {
          payload.edit_scope = editScope;
        }
        const response = await api.put(`/events/${event!.id}`, payload);
        return response.data;
      } else {
        const response = await api.post('/events', payload);
        return response.data;
      }
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['calendar-events'] });
      queryClient.invalidateQueries({ queryKey: ['dashboard'] });
      queryClient.invalidateQueries({ queryKey: ['upcoming'] });
      toast.success(isEditing ? 'Event updated' : 'Event created');
      onClose();
    },
    onError: (error) => {
      toast.error(getErrorMessage(error, isEditing ? 'Failed to update event' : 'Failed to create event'));
    },
  });

  const deleteMutation = useMutation({
    mutationFn: async () => {
      const params = editScope ? `?scope=${editScope}` : '';
      await api.delete(`/events/${event?.id}${params}`);
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['calendar-events'] });
      queryClient.invalidateQueries({ queryKey: ['dashboard'] });
      queryClient.invalidateQueries({ queryKey: ['upcoming'] });
      toast.success('Event deleted');
      onClose();
    },
    onError: (error) => {
      toast.error(getErrorMessage(error, 'Failed to delete event'));
    },
  });

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    mutation.mutate(formData);
  };

  return (
    <Sheet open={true} onOpenChange={onClose}>
      <SheetContent>
        <SheetClose onClick={onClose} />
        <SheetHeader>
          <SheetTitle>
            {isEditing
              ? 'Edit Event'
              : templateName
                ? `Create Event from ${templateName} Template`
                : 'New Event'}
          </SheetTitle>
        </SheetHeader>
        <form onSubmit={handleSubmit} className="flex flex-col flex-1 overflow-y-auto">
          <div className="px-6 py-5 space-y-4 flex-1">
            <div className="space-y-2">
              <Label htmlFor="title" required>Title</Label>
              <Input
                id="title"
                value={formData.title}
                onChange={(e) => setFormData({ ...formData, title: e.target.value })}
                required
              />
            </div>

            <div className="space-y-2">
              <Label htmlFor="description">Description</Label>
              <Textarea
                id="description"
                value={formData.description}
                onChange={(e) => setFormData({ ...formData, description: e.target.value })}
                className="min-h-[80px] flex-1"
              />
            </div>

            <div className="flex items-center gap-2">
              <Checkbox
                id="all_day"
                checked={formData.all_day}
                onChange={(e) => {
                  const checked = (e.target as HTMLInputElement).checked;
                  setFormData({
                    ...formData,
                    all_day: checked,
                    start_datetime: formatForInput(formData.start_datetime, checked, '09:00'),
                    end_datetime: formatForInput(formData.end_datetime, checked, '10:00'),
                  });
                }}
              />
              <Label htmlFor="all_day">All day event</Label>
            </div>

            <div className="grid grid-cols-2 gap-4">
              <div className="space-y-2">
                <Label htmlFor="start" required>Start</Label>
                <Input
                  id="start"
                  type={formData.all_day ? 'date' : 'datetime-local'}
                  value={formData.start_datetime}
                  onChange={(e) => setFormData({ ...formData, start_datetime: e.target.value })}
                  required
                />
              </div>

              <div className="space-y-2">
                <Label htmlFor="end">End</Label>
                <Input
                  id="end"
                  type={formData.all_day ? 'date' : 'datetime-local'}
                  value={formData.end_datetime}
                  onChange={(e) => setFormData({ ...formData, end_datetime: e.target.value })}
                />
              </div>
            </div>

            <div className="grid grid-cols-2 gap-4">
              <div className="space-y-2">
                <Label htmlFor="calendar">Calendar</Label>
                <Select
                  id="calendar"
                  value={formData.calendar_id}
                  onChange={(e) => setFormData({ ...formData, calendar_id: e.target.value })}
                >
                  {selectableCalendars.map((cal) => (
                    <option key={cal.id} value={cal.id}>
                      {cal.name}
                    </option>
                  ))}
                </Select>
              </div>

              <div className="space-y-2">
                <Label htmlFor="location">Location</Label>
                <LocationPicker
                  id="location"
                  value={locationSearch}
                  onChange={(val) => {
                    setLocationSearch(val);
                    if (!val) setFormData({ ...formData, location_id: '' });
                  }}
                  onSelect={async (result) => {
                    if (result.source === 'local' && result.location_id) {
                      setFormData({ ...formData, location_id: result.location_id.toString() });
                    } else if (result.source === 'nominatim') {
                      try {
                        const { data: newLoc } = await api.post('/locations', {
                          name: result.name,
                          address: result.address,
                          category: 'other',
                        });
                        queryClient.invalidateQueries({ queryKey: ['locations'] });
                        setFormData({ ...formData, location_id: newLoc.id.toString() });
                        toast.success(`Location "${result.name}" created`);
                      } catch {
                        toast.error('Failed to create location');
                      }
                    }
                  }}
                  placeholder="Search for a location..."
                />
              </div>
            </div>

            {/* Recurrence picker */}
            <div className="space-y-2">
              <Label htmlFor="recurrence_type">Recurrence</Label>
              <Select
                id="recurrence_type"
                value={recurrenceType}
                onChange={(e) => setRecurrenceType(e.target.value)}
              >
                <option value="">None</option>
                <option value="every_n_days">Every X days</option>
                <option value="weekly">Weekly</option>
                <option value="monthly_nth_weekday">Monthly (nth weekday)</option>
                <option value="monthly_date">Monthly (date)</option>
              </Select>
            </div>

            {recurrenceType === 'every_n_days' && (
              <div className="space-y-2">
                <Label htmlFor="interval">Every how many days?</Label>
                <Input
                  id="interval"
                  type="number"
                  min={1}
                  max={365}
                  value={recurrenceInterval}
                  onChange={(e) => setRecurrenceInterval(parseInt(e.target.value) || 1)}
                />
              </div>
            )}

            {recurrenceType === 'weekly' && (
              <p className="text-xs text-muted-foreground">
                Repeats every week on the same day as the start date.
              </p>
            )}

            {recurrenceType === 'monthly_nth_weekday' && (
              <div className="grid grid-cols-2 gap-4">
                <div className="space-y-2">
                  <Label htmlFor="week">Week of month</Label>
                  <Select
                    id="week"
                    value={recurrenceWeek.toString()}
                    onChange={(e) => setRecurrenceWeek(parseInt(e.target.value))}
                  >
                    <option value="1">1st</option>
                    <option value="2">2nd</option>
                    <option value="3">3rd</option>
                    <option value="4">4th</option>
                  </Select>
                </div>
                <div className="space-y-2">
                  <Label htmlFor="weekday_nth">Day of week</Label>
                  <Select
                    id="weekday_nth"
                    value={recurrenceWeekday.toString()}
                    onChange={(e) => setRecurrenceWeekday(parseInt(e.target.value))}
                  >
                    {WEEKDAYS.map((name, i) => (
                      <option key={i} value={i}>{name}</option>
                    ))}
                  </Select>
                </div>
              </div>
            )}

            {recurrenceType === 'monthly_date' && (
              <div className="space-y-2">
                <Label htmlFor="day">Day of month</Label>
                <Input
                  id="day"
                  type="number"
                  min={1}
                  max={31}
                  value={recurrenceDay}
                  onChange={(e) => setRecurrenceDay(parseInt(e.target.value) || 1)}
                />
              </div>
            )}

            <div className="flex items-center gap-2">
              <Checkbox
                id="is_starred"
                checked={formData.is_starred}
                onChange={(e) => setFormData({ ...formData, is_starred: (e.target as HTMLInputElement).checked })}
              />
              <Label htmlFor="is_starred">Star this event</Label>
            </div>
          </div>

          <SheetFooter>
            {isEditing && (
              <Button
                type="button"
                variant="destructive"
                onClick={() => deleteMutation.mutate()}
                disabled={deleteMutation.isPending}
                className="mr-auto"
              >
                Delete
              </Button>
            )}
            <Button type="button" variant="outline" onClick={onClose}>
              Cancel
            </Button>
            <Button type="submit" disabled={mutation.isPending}>
              {mutation.isPending ? 'Saving...' : isEditing ? 'Update' : 'Create'}
            </Button>
          </SheetFooter>
        </form>
      </SheetContent>
    </Sheet>
  );
}
