import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Field, FieldError, FieldLabel } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { ApiClientError, apiClient } from '@/lib/api-client';
import { zodResolver } from '@hookform/resolvers/zod';
import { type Court, type CreateCourtInput, createCourtSchema } from '@repo/api-contract';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useMemo, useState } from 'react';
import { Controller, useFieldArray, useForm } from 'react-hook-form';
import { DAY_OPTIONS } from '../lib/court.constants';
import { createDefaultCourtValues } from '../lib/court.utils';

type CourtFormSectionProps = {
  complexId: string | null;
  editingCourt: Court | null;
  initialDraft: CreateCourtInput | null;
  onCancelEdit: () => void;
};

export function CourtFormSection({
  complexId,
  editingCourt,
  initialDraft,
  onCancelEdit,
}: CourtFormSectionProps) {
  const queryClient = useQueryClient();
  const [formError, setFormError] = useState<string | null>(null);
  const [isWeekModalOpen, setIsWeekModalOpen] = useState(false);
  const [weekDefaultStartTime, setWeekDefaultStartTime] = useState('08:00');
  const [weekDefaultEndTime, setWeekDefaultEndTime] = useState('22:00');
  const [weekModalError, setWeekModalError] = useState<string | null>(null);

  const sportsQuery = useQuery({
    queryKey: ['sports'],
    queryFn: () => apiClient.sports.list(),
  });

  const form = useForm<CreateCourtInput>({
    resolver: zodResolver(createCourtSchema),
    mode: 'onBlur',
    defaultValues: createDefaultCourtValues(),
  });

  const availabilityFieldArray = useFieldArray({
    control: form.control,
    name: 'availability',
  });

  const saveMutation = useMutation({
    mutationFn: async (payload: CreateCourtInput) => {
      if (editingCourt) {
        return apiClient.courts.update(editingCourt.id, payload);
      }
      if (!complexId) {
        throw new ApiClientError('No se pudo resolver el complejo para guardar la cancha.');
      }
      return apiClient.courts.create(complexId, payload);
    },
    onSuccess: () => {
      void queryClient.invalidateQueries({
        queryKey: ['courts', complexId],
      });
      setFormError(null);
      if (editingCourt) {
        onCancelEdit();
      }
      form.reset(createDefaultCourtValues());
    },
    onError: (error) => {
      const message =
        error instanceof ApiClientError
          ? error.message
          : 'No se pudo guardar la cancha. Intenta nuevamente.';
      setFormError(message);
    },
  });

  const activeSports = useMemo(
    () => (sportsQuery.data ?? []).filter((sport) => sport.isActive),
    [sportsQuery.data]
  );

  const onSubmit = async (values: CreateCourtInput) => {
    setFormError(null);
    await saveMutation.mutateAsync(values);
  };

  useEffect(() => {
    if (editingCourt) {
      form.reset({
        name: editingCourt.name,
        sportId: editingCourt.sportId,
        slotDurationMinutes: editingCourt.slotDurationMinutes,
        basePrice: editingCourt.basePrice,
        availability: editingCourt.availability.map((slot) => ({
          dayOfWeek: slot.dayOfWeek,
          startTime: slot.startTime,
          endTime: slot.endTime,
        })),
      });
      return;
    }

    if (initialDraft) {
      form.reset(initialDraft);
      return;
    }

    form.reset(createDefaultCourtValues());
  }, [editingCourt, form, initialDraft]);

  const title = editingCourt ? 'Editar cancha' : 'Nueva cancha';
  const buttonText = editingCourt ? 'Guardar cambios' : 'Agregar cancha';

  return (
    <section className="mb-4 rounded-xl border bg-card p-3 text-card-foreground shadow-sm sm:p-5">
      <h3 className="text-lg font-semibold">{title}</h3>
      <p className="mt-1 text-sm text-muted-foreground">
        Configura nombre, deporte, duración, precio base y horarios. El backend ya contempla precios
        por franja horaria para la siguiente etapa.
      </p>

      <form
        className="mt-4 space-y-4"
        onSubmit={form.handleSubmit((values) => {
          void onSubmit(values);
        })}
      >
        <Field data-invalid={Boolean(form.formState.errors.name)}>
          <FieldLabel htmlFor="court-name">Nombre o descripción</FieldLabel>
          <Input
            id="court-name"
            placeholder="Ej: Futbol 5 - Cancha 1"
            aria-invalid={Boolean(form.formState.errors.name)}
            {...form.register('name')}
          />
          <FieldError errors={[form.formState.errors.name]} />
        </Field>

        <div className="grid gap-4 md:grid-cols-3">
          <Field data-invalid={Boolean(form.formState.errors.sportId)}>
            <FieldLabel>Deporte</FieldLabel>
            <Controller
              control={form.control}
              name="sportId"
              render={({ field }) => (
                <Select
                  value={field.value}
                  onValueChange={field.onChange}
                  disabled={sportsQuery.isLoading}
                >
                  <SelectTrigger className="h-10 w-full">
                    <SelectValue placeholder="Selecciona un deporte" />
                  </SelectTrigger>
                  <SelectContent>
                    {activeSports.map((sport) => (
                      <SelectItem key={sport.id} value={sport.id}>
                        {sport.name}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              )}
            />
            <FieldError errors={[form.formState.errors.sportId]} />
          </Field>

          <Field data-invalid={Boolean(form.formState.errors.slotDurationMinutes)}>
            <FieldLabel htmlFor="slot-duration">Duración por turno (min)</FieldLabel>
            <Input
              id="slot-duration"
              type="number"
              min={15}
              step={5}
              aria-invalid={Boolean(form.formState.errors.slotDurationMinutes)}
              {...form.register('slotDurationMinutes', { valueAsNumber: true })}
            />
            <FieldError errors={[form.formState.errors.slotDurationMinutes]} />
          </Field>

          <Field data-invalid={Boolean(form.formState.errors.basePrice)}>
            <FieldLabel htmlFor="base-price">Precio base</FieldLabel>
            <Input
              id="base-price"
              type="number"
              min={0}
              step="0.01"
              aria-invalid={Boolean(form.formState.errors.basePrice)}
              {...form.register('basePrice', { valueAsNumber: true })}
            />
            <FieldError errors={[form.formState.errors.basePrice]} />
          </Field>
        </div>

        <div className="space-y-3 rounded-lg border p-3 sm:p-4">
          <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <h4 className="text-sm font-semibold">Rangos horarios</h4>
              <p className="text-xs text-muted-foreground">Define uno o más rangos por día.</p>
            </div>
            <div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
              <Button
                type="button"
                variant="outline"
                className="w-full sm:w-auto"
                onClick={() => {
                  const availability = form.getValues('availability');
                  setWeekDefaultStartTime(availability[0]?.startTime ?? '08:00');
                  setWeekDefaultEndTime(availability[0]?.endTime ?? '22:00');
                  setWeekModalError(null);
                  setIsWeekModalOpen(true);
                }}
              >
                Agregar semana completa
              </Button>
              <Button
                type="button"
                variant="outline"
                className="w-full sm:w-auto"
                onClick={() => {
                  availabilityFieldArray.append({
                    dayOfWeek: 'MONDAY',
                    startTime: '08:00',
                    endTime: '22:00',
                  });
                }}
              >
                Agregar rango
              </Button>
            </div>
          </div>

          <div className="space-y-2">
            {availabilityFieldArray.fields.map((field, index) => (
              <div
                key={field.id}
                className="grid gap-3 border-b pb-2 md:grid-cols-[1fr_1fr_1fr_auto]"
              >
                <Field
                  data-invalid={Boolean(form.formState.errors.availability?.[index]?.dayOfWeek)}
                >
                  <FieldLabel>Día</FieldLabel>
                  <Controller
                    control={form.control}
                    name={`availability.${index}.dayOfWeek`}
                    render={({ field: dayField }) => (
                      <Select value={dayField.value} onValueChange={dayField.onChange}>
                        <SelectTrigger className="h-10 w-full">
                          <SelectValue placeholder="Día" />
                        </SelectTrigger>
                        <SelectContent>
                          {DAY_OPTIONS.map((option) => (
                            <SelectItem key={option.value} value={option.value}>
                              {option.label}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    )}
                  />
                  <FieldError errors={[form.formState.errors.availability?.[index]?.dayOfWeek]} />
                </Field>

                <Field
                  data-invalid={Boolean(form.formState.errors.availability?.[index]?.startTime)}
                >
                  <FieldLabel>Desde</FieldLabel>
                  <Input
                    type="time"
                    aria-invalid={Boolean(form.formState.errors.availability?.[index]?.startTime)}
                    {...form.register(`availability.${index}.startTime`)}
                  />
                  <FieldError errors={[form.formState.errors.availability?.[index]?.startTime]} />
                </Field>

                <Field data-invalid={Boolean(form.formState.errors.availability?.[index]?.endTime)}>
                  <FieldLabel>Hasta</FieldLabel>
                  <Input
                    type="time"
                    aria-invalid={Boolean(form.formState.errors.availability?.[index]?.endTime)}
                    {...form.register(`availability.${index}.endTime`)}
                  />
                  <FieldError errors={[form.formState.errors.availability?.[index]?.endTime]} />
                </Field>

                <div className="flex items-end">
                  <Button
                    type="button"
                    variant="ghost"
                    className="w-full md:w-auto"
                    disabled={availabilityFieldArray.fields.length === 1}
                    onClick={() => {
                      availabilityFieldArray.remove(index);
                    }}
                  >
                    Quitar
                  </Button>
                </div>
              </div>
            ))}
          </div>
        </div>

        {formError && <p className="text-sm text-destructive">{formError}</p>}

        <div className="flex flex-wrap items-center gap-2">
          <Button
            type="submit"
            disabled={saveMutation.isPending || sportsQuery.isLoading || !complexId}
          >
            {saveMutation.isPending ? 'Guardando...' : buttonText}
          </Button>
          {editingCourt && (
            <Button type="button" variant="outline" onClick={onCancelEdit}>
              Cancelar edición
            </Button>
          )}
        </div>
      </form>

      <Dialog
        open={isWeekModalOpen}
        onOpenChange={(nextOpen) => {
          setIsWeekModalOpen(nextOpen);
          if (!nextOpen) {
            setWeekModalError(null);
          }
        }}
      >
        <DialogContent className="max-w-md">
          <DialogHeader>
            <DialogTitle>Horario por defecto</DialogTitle>
            <DialogDescription>
              Ingresa el horario para completar los días faltantes de la semana.
            </DialogDescription>
          </DialogHeader>

          <div className="grid gap-3 sm:grid-cols-2">
            <Field>
              <FieldLabel htmlFor="week-start-time">Desde</FieldLabel>
              <Input
                id="week-start-time"
                type="time"
                value={weekDefaultStartTime}
                onChange={(event) => {
                  setWeekDefaultStartTime(event.target.value);
                  setWeekModalError(null);
                }}
              />
            </Field>

            <Field>
              <FieldLabel htmlFor="week-end-time">Hasta</FieldLabel>
              <Input
                id="week-end-time"
                type="time"
                value={weekDefaultEndTime}
                onChange={(event) => {
                  setWeekDefaultEndTime(event.target.value);
                  setWeekModalError(null);
                }}
              />
            </Field>
          </div>

          {weekModalError && <p className="text-sm text-destructive">{weekModalError}</p>}

          <DialogFooter>
            <Button
              type="button"
              variant="outline"
              onClick={() => {
                setIsWeekModalOpen(false);
                setWeekModalError(null);
              }}
            >
              Cancelar
            </Button>
            <Button
              type="button"
              onClick={() => {
                if (weekDefaultStartTime >= weekDefaultEndTime) {
                  setWeekModalError('El horario de inicio debe ser menor al horario de fin.');
                  return;
                }

                const availability = form.getValues('availability');
                const existingDays = new Set(availability.map((item) => item.dayOfWeek));

                const missingDays = DAY_OPTIONS.filter(
                  (option) => !existingDays.has(option.value)
                ).map((option) => ({
                  dayOfWeek: option.value,
                  startTime: weekDefaultStartTime,
                  endTime: weekDefaultEndTime,
                }));

                if (missingDays.length > 0) {
                  availabilityFieldArray.append(missingDays);
                }

                setIsWeekModalOpen(false);
                setWeekModalError(null);
              }}
            >
              Agregar días faltantes
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </section>
  );
}
