"use client";

import dynamic from "next/dynamic";
import { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { createPortal } from "react-dom";

const MapPicker = dynamic(() => import("./MapPicker"), { ssr: false });

type Category = {
  id: string;
  name: string;
};

type PlaceResult = {
  description: string;
  place_id: string;
};

type EventFormProps = {
  variant?: "card" | "inline";
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
  prefillStartAt?: string | Date | null;
  showTrigger?: boolean;
};

export default function EventForm({
  variant = "card",
  open,
  onOpenChange,
  prefillStartAt,
  showTrigger = true
}: EventFormProps) {
  const { data } = useSession();
  const [status, setStatus] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [categories, setCategories] = useState<Category[]>([]);
  const [isOpen, setIsOpen] = useState(false);
  const [startAt, setStartAt] = useState("");
  const [endAt, setEndAt] = useState("");
  const [placesProvider, setPlacesProvider] = useState<"google" | "osm">("osm");
  const [placesKey, setPlacesKey] = useState("");
  const [placeQuery, setPlaceQuery] = useState("");
  const [placeResults, setPlaceResults] = useState<PlaceResult[]>([]);
  const [placeId, setPlaceId] = useState<string | null>(null);
  const [placeLat, setPlaceLat] = useState<number | null>(null);
  const [placeLng, setPlaceLng] = useState<number | null>(null);
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  useEffect(() => {
    const loadCategories = async () => {
      try {
        const response = await fetch("/api/categories");
        if (!response.ok) return;
        setCategories(await response.json());
      } catch {
        // Ignore category load errors in UI.
      }
    };

    loadCategories();
  }, []);

  const isControlled = open !== undefined;
  const modalOpen = isControlled ? open : isOpen;
  const setModalOpen = (value: boolean) => {
    if (isControlled) {
      onOpenChange?.(value);
    } else {
      setIsOpen(value);
    }
  };

  const formatLocalDateTime = (value: Date) => {
    const offset = value.getTimezoneOffset() * 60000;
    return new Date(value.getTime() - offset).toISOString().slice(0, 16);
  };

  useEffect(() => {
    const loadKey = async () => {
      try {
      const response = await fetch("/api/settings/system");
        if (!response.ok) return;
        const payload = await response.json();
        setPlacesKey(payload.apiKey || "");
        setPlacesProvider(payload.provider === "google" ? "google" : "osm");
      } catch {
        // ignore
      }
    };

    if (modalOpen) {
      loadKey();
    }
  }, [modalOpen]);

  useEffect(() => {
    const fetchPlaces = async () => {
      if (placeQuery.trim().length < 3) {
        setPlaceResults([]);
        return;
      }

      if (placesProvider === "google" && !placesKey) {
        setPlaceResults([]);
        return;
      }

      const response = await fetch(
        `/api/places/autocomplete?input=${encodeURIComponent(placeQuery)}&countries=de,fr,ch,at`
      );
      if (!response.ok) return;
      const payload = await response.json();
      setPlaceResults(payload.predictions || []);
    };

    const timer = window.setTimeout(() => {
      fetchPlaces();
    }, 350);
    return () => window.clearTimeout(timer);
  }, [placeQuery, placesKey, placesProvider]);

  useEffect(() => {
    if (!modalOpen) return;
    if (prefillStartAt) {
      const startDate =
        prefillStartAt instanceof Date
          ? prefillStartAt
          : new Date(prefillStartAt);
      if (Number.isNaN(startDate.getTime())) return;
      const startValue = formatLocalDateTime(startDate);
      setStartAt(startValue);
      const endDate = new Date(startDate.getTime() + 3 * 60 * 60 * 1000);
      setEndAt(formatLocalDateTime(endDate));
      return;
    }

    const startDate = new Date();
    startDate.setHours(12, 0, 0, 0);
    setStartAt(formatLocalDateTime(startDate));
    const endDate = new Date(startDate.getTime() + 3 * 60 * 60 * 1000);
    setEndAt(formatLocalDateTime(endDate));
  }, [modalOpen, prefillStartAt]);

  const selectPlace = async (place: PlaceResult) => {
    setPlaceResults([]);
    setPlaceQuery(place.description);
    setPlaceId(place.place_id);

    const response = await fetch(
      `/api/places/details?placeId=${encodeURIComponent(place.place_id)}`
    );
    if (!response.ok) return;
    const payload = await response.json();
    setPlaceLat(payload.lat ?? null);
    setPlaceLng(payload.lng ?? null);
  };

  const reverseLookup = async (lat: number, lng: number) => {
    try {
      const response = await fetch(
        `/api/places/reverse?lat=${lat}&lng=${lng}`
      );
      if (!response.ok) return;
      const payload = await response.json();
      if (payload.label) {
        setPlaceQuery(payload.label);
      }
      if (payload.placeId) {
        setPlaceId(payload.placeId);
      }
    } catch {
      // ignore
    }
  };

  const pickOnMap = (lat: number, lng: number) => {
    setPlaceLat(lat);
    setPlaceLng(lng);
    setPlaceResults([]);
    reverseLookup(lat, lng);
  };

  const toIsoString = (value: FormDataEntryValue | null) => {
    if (!value) return null;
    const raw = String(value);
    if (!raw) return null;
    const date = new Date(raw);
    if (Number.isNaN(date.getTime())) return null;
    return date.toISOString();
  };

  const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const form = event.currentTarget;
    setStatus(null);
    setError(null);

    const formData = new FormData(form);
    const payload = {
      title: formData.get("title"),
      description: formData.get("description"),
      location: formData.get("location"),
      locationPlaceId: formData.get("locationPlaceId"),
      locationLat: formData.get("locationLat"),
      locationLng: formData.get("locationLng"),
      startAt: toIsoString(formData.get("startAt")),
      endAt: toIsoString(formData.get("endAt")),
      categoryId: formData.get("categoryId")
    };

    try {
      const response = await fetch("/api/events", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload)
      });

      if (!response.ok) {
        const data = await response.json();
        throw new Error(data.error || "Fehler beim Speichern.");
      }

      form.reset();
      setStartAt("");
      setEndAt("");
      setStatus("Termin vorgeschlagen. Ein Admin bestätigt ihn.");
      setModalOpen(false);
      window.dispatchEvent(new Event("views-updated"));
      setPlaceQuery("");
      setPlaceResults([]);
      setPlaceId(null);
      setPlaceLat(null);
      setPlaceLng(null);
    } catch (err) {
      setError((err as Error).message);
    }
  };

  const triggerButton = showTrigger ? (
    <button
      type="button"
      className="btn-accent"
      onClick={() => {
        setModalOpen(true);
        setPlaceQuery("");
        setPlaceResults([]);
        setPlaceId(null);
        setPlaceLat(null);
        setPlaceLng(null);
      }}
    >
      Neuer Termin
    </button>
  ) : null;

  return (
    <>
      {variant === "card" ? (
        <section className="card fade-up-delay">
          <div className="flex flex-wrap items-start justify-between gap-3">
            {triggerButton}
          </div>
          {categories.length === 0 && (
            <p className="mt-3 text-sm text-amber-600">
              Noch keine Kategorien vorhanden. Bitte Admin um Anlage.
            </p>
          )}
          {status && <p className="mt-3 text-sm text-emerald-600">{status}</p>}
          {error && <p className="mt-3 text-sm text-red-600">{error}</p>}
        </section>
      ) : triggerButton ? (
        <div className="flex flex-col items-start gap-2">
          {triggerButton}
          {status && <p className="text-xs text-emerald-600">{status}</p>}
          {error && <p className="text-xs text-red-600">{error}</p>}
        </div>
      ) : null}
      {modalOpen &&
        mounted &&
        createPortal(
          <div className="fixed inset-0 z-40 flex items-center justify-center bg-black/50 px-4 py-6">
            <div className="card w-full max-w-xl max-h-[90vh] overflow-y-auto">
            <div className="flex items-center justify-between">
              <h3 className="text-lg font-semibold">Neuen Termin anlegen</h3>
              <button
                type="button"
                className="text-sm text-slate-600"
                onClick={() => setModalOpen(false)}
              >
                Schließen
              </button>
            </div>
            {data?.user?.role === "USER" && (
              <p className="mt-2 text-sm text-slate-600">
                Hinweis: Terminvorschläge müssen von einem Admin freigegeben werden.
              </p>
            )}
            <form onSubmit={onSubmit} className="mt-4 grid gap-3 md:grid-cols-2">
              <input
                name="title"
                placeholder="Titel"
                required
                className="rounded-xl border border-slate-300 px-3 py-2"
              />
              <div className="relative">
              <input
                name="location"
                placeholder="Ort"
                value={placeQuery}
                onChange={(event) => {
                  setPlaceQuery(event.target.value);
                  setPlaceId(null);
                  setPlaceLat(null);
                  setPlaceLng(null);
                }}
                className="w-full rounded-xl border border-slate-300 px-3 py-2"
              />
                {placeResults.length > 0 && (
                  <div className="absolute z-10 mt-2 max-h-56 w-full overflow-y-auto rounded-xl border border-slate-200 bg-white shadow">
                    {placeResults.map((place) => (
                      <button
                        key={place.place_id}
                        type="button"
                        className="block w-full px-3 py-2 text-left text-sm hover:bg-slate-100"
                        onClick={() => selectPlace(place)}
                      >
                        {place.description}
                      </button>
                    ))}
                  </div>
                )}
                {placesProvider === "google" && !placesKey && placeQuery.length > 0 && (
                  <div className="mt-2 text-xs text-slate-500">
                    Google Places ist nicht konfiguriert.
                  </div>
                )}
              </div>
              <div className="md:col-span-2">
                <label className="text-xs uppercase tracking-[0.2em] text-slate-500">
                  Ort per Karte wählen
                </label>
                <div className="mt-2 overflow-hidden rounded-xl border border-slate-200">
                  <MapPicker
                    value={
                      placeLat !== null && placeLng !== null
                        ? { lat: placeLat, lng: placeLng }
                        : null
                    }
                    onChange={(coords) => pickOnMap(coords.lat, coords.lng)}
                  />
                </div>
                <p className="mt-2 text-xs text-slate-500">
                  Klick auf die Karte, um den Ort zu setzen.
                </p>
              </div>
              <input type="hidden" name="locationPlaceId" value={placeId || ""} />
              <input type="hidden" name="locationLat" value={placeLat ?? ""} />
              <input type="hidden" name="locationLng" value={placeLng ?? ""} />
              <input
                name="startAt"
                type="datetime-local"
                required
                value={startAt}
                onChange={(event) => {
                  const nextStart = event.target.value;
                  setStartAt(nextStart);
                  if (nextStart) {
                    const startDate = new Date(nextStart);
                    const endDate = new Date(startDate.getTime() + 3 * 60 * 60 * 1000);
                    const offset = endDate.getTimezoneOffset() * 60000;
                    const local = new Date(endDate.getTime() - offset)
                      .toISOString()
                      .slice(0, 16);
                    setEndAt(local);
                  } else {
                    setEndAt("");
                  }
                }}
                className="rounded-xl border border-slate-300 px-3 py-2"
              />
              <input
                name="endAt"
                type="datetime-local"
                value={endAt}
                onChange={(event) => setEndAt(event.target.value)}
                className="rounded-xl border border-slate-300 px-3 py-2"
              />
              <select
                name="categoryId"
                required
                className="rounded-xl border border-slate-300 px-3 py-2"
                disabled={categories.length === 0}
              >
                <option value="" disabled>
                  Kategorie wählen
                </option>
                {categories.map((category) => (
                  <option key={category.id} value={category.id}>
                    {category.name}
                  </option>
                ))}
              </select>
              <textarea
                name="description"
                placeholder="Beschreibung"
                className="min-h-[96px] rounded-xl border border-slate-300 px-3 py-2 md:col-span-2"
              />
              <button
                type="submit"
                className="btn-accent md:col-span-2"
                disabled={categories.length === 0}
              >
                Vorschlag senden
              </button>
            </form>
            {categories.length === 0 && (
              <p className="mt-3 text-sm text-amber-600">
                Noch keine Kategorien vorhanden. Bitte Admin um Anlage.
              </p>
            )}
            {status && <p className="mt-3 text-sm text-emerald-600">{status}</p>}
            {error && <p className="mt-3 text-sm text-red-600">{error}</p>}
          </div>
        </div>,
          document.body
        )}
    </>
  );
}
