"use client";

import { useState, useEffect, useMemo, useRef, useCallback } from "react";
import type { GeofabrikFeature, GeofabrikIndex } from "@transportationer/shared";
import { CityIngestProgress } from "@/components/city-ingest-progress";

type Step = "browse" | "confirm" | "ingest";

// ─── Step indicator ───────────────────────────────────────────────────────────

function StepIndicator({ current }: { current: Step }) {
  const steps: { key: Step; label: string }[] = [
    { key: "browse", label: "Select Region" },
    { key: "confirm", label: "Confirm" },
    { key: "ingest", label: "Processing" },
  ];
  const idx = steps.findIndex((s) => s.key === current);
  return (
    <nav className="flex items-center gap-4 mb-8">
      {steps.map((s, i) => (
        <div key={s.key} className="flex items-center gap-2">
          <span
            className={`w-6 h-6 rounded-full text-xs flex items-center justify-center font-medium ${
              i < idx
                ? "bg-brand-600 text-white"
                : i === idx
                  ? "bg-brand-600 text-white ring-2 ring-brand-300"
                  : "bg-gray-200 text-gray-500"
            }`}
          >
            {i < idx ? "✓" : i + 1}
          </span>
          <span
            className={`text-sm font-medium ${i === idx ? "text-gray-900" : "text-gray-500"}`}
          >
            {s.label}
          </span>
          {i < steps.length - 1 && (
            <span className="text-gray-300 mx-2">→</span>
          )}
        </div>
      ))}
    </nav>
  );
}

// ─── Geofabrik browser ────────────────────────────────────────────────────────

function GeofabrikBrowser({
  onSelect,
}: {
  onSelect: (f: GeofabrikFeature) => void;
}) {
  const [index, setIndex] = useState<GeofabrikIndex | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [query, setQuery] = useState("");
  const [parent, setParent] = useState<string | undefined>(undefined);

  useEffect(() => {
    fetch("/api/admin/geofabrik")
      .then((r) => r.json())
      .then(setIndex)
      .catch((e) => setError(String(e)))
      .finally(() => setLoading(false));
  }, []);

  const features = useMemo(() => {
    if (!index) return [];
    return index.features.filter((f) => {
      const matchesParent = parent
        ? f.properties.parent === parent
        : !f.properties.parent;
      const matchesQuery = query
        ? f.properties.name.toLowerCase().includes(query.toLowerCase()) ||
          f.properties.id.toLowerCase().includes(query.toLowerCase())
        : true;
      return matchesParent && matchesQuery && f.properties.urls?.pbf;
    });
  }, [index, parent, query]);

  const parentFeature = index?.features.find(
    (f) => f.properties.id === parent,
  );
  const grandParent = parentFeature
    ? index?.features.find(
        (f) => f.properties.id === parentFeature.properties.parent,
      )
    : null;

  if (loading)
    return (
      <div className="card py-12 text-center text-gray-500">
        Loading Geofabrik region index…
      </div>
    );
  if (error)
    return (
      <div className="card py-8 text-center text-red-600">
        Error loading index: {error}
      </div>
    );

  return (
    <div className="card">
      <h2 className="text-lg font-semibold mb-4">Select a Region</h2>

      <div className="flex items-center gap-2 text-sm mb-4 flex-wrap">
        <button
          onClick={() => setParent(undefined)}
          className="text-brand-600 hover:underline"
        >
          All regions
        </button>
        {grandParent && (
          <>
            <span className="text-gray-400">›</span>
            <button
              onClick={() => setParent(grandParent.properties.id)}
              className="text-brand-600 hover:underline"
            >
              {grandParent.properties.name}
            </button>
          </>
        )}
        {parentFeature && (
          <>
            <span className="text-gray-400">›</span>
            <span className="font-medium">{parentFeature.properties.name}</span>
          </>
        )}
      </div>

      <input
        type="search"
        placeholder="Search regions…"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        className="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm mb-4 focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500"
      />

      <div className="divide-y divide-gray-100 max-h-80 overflow-y-auto rounded border border-gray-200">
        {features.length === 0 ? (
          <p className="text-sm text-gray-500 py-4 px-3">No regions found.</p>
        ) : (
          features.map((f) => {
            const hasChildren = index!.features.some(
              (c) => c.properties.parent === f.properties.id,
            );
            return (
              <div
                key={f.properties.id}
                className="flex items-center justify-between px-3 py-2.5 hover:bg-gray-50"
              >
                <div>
                  <p className="text-sm font-medium text-gray-900">
                    {f.properties.name}
                  </p>
                  <p className="text-xs text-gray-400">{f.properties.id}</p>
                </div>
                <div className="flex gap-2">
                  {hasChildren && (
                    <button
                      onClick={() => {
                        setParent(f.properties.id);
                        setQuery("");
                      }}
                      className="btn-secondary text-xs py-1"
                    >
                      Browse →
                    </button>
                  )}
                  <button
                    onClick={() => onSelect(f)}
                    className="btn-primary text-xs py-1"
                  >
                    Select
                  </button>
                </div>
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}

// ─── Nominatim polygon geocoder + mini map ───────────────────────────────────

interface NominatimResult {
  place_id: number;
  display_name: string;
  lat: string;
  lon: string;
  type: string;
  class: string;
  geojson?: { type: string; coordinates: unknown };
}

function LocationSelector({
  regionGeometry,
  onBoundaryChange,
}: {
  regionGeometry: GeofabrikFeature["geometry"];
  onBoundaryChange: (boundary: { type: string; coordinates: unknown } | null) => void;
}) {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<NominatimResult[]>([]);
  const [selected, setSelected] = useState<NominatimResult | null>(null);
  const [showDropdown, setShowDropdown] = useState(false);
  const [searching, setSearching] = useState(false);
  const mapContainerRef = useRef<HTMLDivElement>(null);
  const mapRef = useRef<import("maplibre-gl").Map | null>(null);
  const mapReadyRef = useRef(false);

  const onBoundaryChangeRef = useRef(onBoundaryChange);
  onBoundaryChangeRef.current = onBoundaryChange;

  // Notify parent when selection changes
  useEffect(() => {
    onBoundaryChangeRef.current(selected?.geojson ?? null);
  }, [selected]);

  // Debounced Nominatim search — request polygon_geojson + featuretype=settlement
  useEffect(() => {
    if (query.length < 2) {
      setResults([]);
      return;
    }
    const timer = setTimeout(async () => {
      setSearching(true);
      try {
        const res = await fetch(
          `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(query)}&polygon_geojson=1&featuretype=settlement&format=json&limit=8`,
          { headers: { "User-Agent": "Transportationer/1.0 (15-minute city analyzer)" } },
        );
        const data: NominatimResult[] = await res.json();
        // Keep only results that have a real polygon boundary
        const polygons = data.filter(
          (r) => r.geojson?.type === "Polygon" || r.geojson?.type === "MultiPolygon",
        );
        setResults(polygons);
        setShowDropdown(true);
      } catch {
        setResults([]);
      } finally {
        setSearching(false);
      }
    }, 500);
    return () => clearTimeout(timer);
  }, [query]);

  // Apply boundary polygon to mini map
  const applyBoundaryToMap = useCallback(
    (map: import("maplibre-gl").Map, geojson: NominatimResult["geojson"] | null) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const src = map.getSource("area-boundary") as any;
      if (!src) return;
      if (geojson) {
        src.setData({ type: "Feature", geometry: geojson, properties: {} });
        // Fit to boundary bbox
        try {
          const coords: number[][] =
            geojson.type === "Polygon"
              ? (geojson.coordinates as number[][][])[0]
              : (geojson.coordinates as number[][][][])[0][0];
          const lngs = coords.map((c) => c[0]);
          const lats = coords.map((c) => c[1]);
          map.fitBounds(
            [Math.min(...lngs), Math.min(...lats), Math.max(...lngs), Math.max(...lats)],
            { padding: 40, duration: 500 },
          );
        } catch { /* ignore */ }
      } else {
        src.setData({ type: "FeatureCollection", features: [] });
      }
    },
    [],
  );

  // Update mini map when selection changes
  useEffect(() => {
    const map = mapRef.current;
    if (map && mapReadyRef.current) applyBoundaryToMap(map, selected?.geojson ?? null);
  }, [selected, applyBoundaryToMap]);

  // Initialize mini map
  useEffect(() => {
    if (!mapContainerRef.current) return;
    let cancelled = false;

    (async () => {
      const mgl = await import("maplibre-gl");
      const { Protocol } = await import("pmtiles");

      if (cancelled) return;

      // Register pmtiles protocol (idempotent)
      try {
        const p = new Protocol();
        mgl.addProtocol("pmtiles", p.tile);
      } catch {
        // Already registered
      }

      const map = new mgl.Map({
        container: mapContainerRef.current!,
        style: "/tiles/style.json",
        center: [10, 51],
        zoom: 3,
      });
      mapRef.current = map;

      map.on("load", () => {
        if (cancelled) return;
        mapReadyRef.current = true;

        // Region outline (Geofabrik polygon)
        map.addSource("area-region", {
          type: "geojson",
          data: regionGeometry
            ? { type: "Feature", geometry: regionGeometry, properties: {} }
            : { type: "FeatureCollection", features: [] },
        });
        map.addLayer({
          id: "area-region-fill",
          type: "fill",
          source: "area-region",
          paint: { "fill-color": "#64748b", "fill-opacity": 0.1 },
        });
        map.addLayer({
          id: "area-region-line",
          type: "line",
          source: "area-region",
          paint: { "line-color": "#64748b", "line-width": 1.5, "line-dasharray": [3, 2] },
        });

        // Selected city boundary polygon
        map.addSource("area-boundary", {
          type: "geojson",
          data: { type: "FeatureCollection", features: [] },
        });
        map.addLayer({
          id: "area-boundary-fill",
          type: "fill",
          source: "area-boundary",
          paint: { "fill-color": "#2563eb", "fill-opacity": 0.15 },
        });
        map.addLayer({
          id: "area-boundary-line",
          type: "line",
          source: "area-boundary",
          paint: { "line-color": "#2563eb", "line-width": 2 },
        });

        // Fit to region if available
        if (regionGeometry) {
          try {
            const coords: number[][] =
              regionGeometry.type === "Polygon"
                ? regionGeometry.coordinates[0]
                : regionGeometry.coordinates[0][0];
            const lngs = coords.map((c) => c[0]);
            const lats = coords.map((c) => c[1]);
            map.fitBounds(
              [Math.min(...lngs), Math.min(...lats), Math.max(...lngs), Math.max(...lats)],
              { padding: 20 },
            );
          } catch { /* ignore */ }
        }
      });
    })();

    return () => {
      cancelled = true;
      mapReadyRef.current = false;
      mapRef.current?.remove();
      mapRef.current = null;
    };
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (
    <div className="space-y-3">
      {/* Geocoder */}
      <div className="relative">
        <div className="flex gap-2 items-center">
          <input
            type="search"
            placeholder="Search for a city or municipality…"
            value={query}
            onChange={(e) => {
              setQuery(e.target.value);
              if (!e.target.value) { setSelected(null); setResults([]); }
            }}
            onFocus={() => results.length > 0 && setShowDropdown(true)}
            className="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500"
          />
          {searching && (
            <span className="text-xs text-gray-400 whitespace-nowrap">Searching…</span>
          )}
        </div>

        {showDropdown && results.length > 0 && (
          <div className="absolute z-20 left-0 right-0 top-full mt-1 bg-white border border-gray-200 rounded-md shadow-lg max-h-56 overflow-y-auto">
            {results.map((r) => (
              <button
                key={r.place_id}
                type="button"
                onClick={() => {
                  setSelected(r);
                  setQuery(r.display_name.split(",").slice(0, 3).join(", "));
                  setShowDropdown(false);
                }}
                className="w-full text-left px-3 py-2 text-sm hover:bg-gray-50 border-b border-gray-100 last:border-0"
              >
                <span className="font-medium text-gray-800">
                  {r.display_name.split(",")[0]}
                </span>
                <span className="text-gray-400 ml-1 text-xs">
                  {r.display_name.split(",").slice(1, 3).join(",")}
                </span>
                <span className="ml-2 text-xs text-blue-500">{r.geojson?.type}</span>
              </button>
            ))}
          </div>
        )}
      </div>

      {/* Mini map */}
      <div
        ref={mapContainerRef}
        className="w-full rounded-md border border-gray-200 overflow-hidden"
        style={{ height: 220 }}
      />

      {selected?.geojson && (
        <p className="text-xs text-green-700">
          ✓ Boundary: {selected.display_name.split(",").slice(0, 2).join(", ")} ({selected.geojson.type})
        </p>
      )}
      {!selected && (
        <p className="text-xs text-gray-400">
          Search for a city to use its administrative boundary for stats coverage, or leave empty to use the entire dataset.
        </p>
      )}
    </div>
  );
}

// ─── Confirm step ─────────────────────────────────────────────────────────────

function ConfirmStep({
  region,
  onBack,
  onConfirm,
}: {
  region: GeofabrikFeature;
  onBack: () => void;
  onConfirm: (
    slug: string,
    name: string,
    countryCode: string,
    boundary: { type: string; coordinates: unknown } | null,
  ) => Promise<void>;
}) {
  const defaultSlug = region.properties.id.replace(/\//g, "-");
  const [slug, setSlug] = useState(defaultSlug);
  const [name, setName] = useState(region.properties.name);
  const [countryCode, setCountryCode] = useState("");
  const [boundary, setBoundary] = useState<{ type: string; coordinates: unknown } | null>(null);
  const [loading, setLoading] = useState(false);

  const handleBoundaryChange = useCallback(
    (b: { type: string; coordinates: unknown } | null) => setBoundary(b),
    [],
  );

  return (
    <div className="card max-w-2xl">
      <h2 className="text-lg font-semibold mb-4">Confirm City Details</h2>

      <div className="space-y-4">
        <div className="grid grid-cols-2 gap-4">
          <div>
            <label className="block text-sm font-medium text-gray-700 mb-1">
              Display Name
            </label>
            <input
              value={name}
              onChange={(e) => setName(e.target.value)}
              className="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500"
            />
          </div>
          <div>
            <label className="block text-sm font-medium text-gray-700 mb-1">
              Slug <span className="text-gray-400">(URL identifier)</span>
            </label>
            <input
              value={slug}
              onChange={(e) =>
                setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, "-"))
              }
              className="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm font-mono focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500"
            />
          </div>
        </div>

        <div className="w-24">
          <label className="block text-sm font-medium text-gray-700 mb-1">
            Country Code <span className="text-gray-400">(2-letter)</span>
          </label>
          <input
            value={countryCode}
            maxLength={2}
            onChange={(e) => setCountryCode(e.target.value.toUpperCase())}
            className="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500"
            placeholder="DE"
          />
        </div>

        {/* City boundary selector */}
        <div>
          <label className="block text-sm font-medium text-gray-700 mb-2">
            City Boundary{" "}
            <span className="text-gray-400 font-normal">(optional — for accurate coverage stats)</span>
          </label>
          <p className="text-xs text-gray-500 mb-3">
            Search for the city municipality to use its administrative boundary for
            coverage statistics. The bounding box is still used for OSM import, routing,
            and grid generation.
          </p>
          <LocationSelector
            regionGeometry={region.geometry}
            onBoundaryChange={handleBoundaryChange}
          />
        </div>

        <div className="bg-gray-50 rounded-md p-3">
          <p className="text-xs text-gray-500 font-medium mb-1">Source URL</p>
          <p className="text-xs text-gray-700 break-all">
            {region.properties.urls.pbf}
          </p>
        </div>
      </div>

      <div className="flex gap-3 mt-6">
        <button onClick={onBack} className="btn-secondary">
          Back
        </button>
        <button
          onClick={async () => {
            setLoading(true);
            await onConfirm(slug, name, countryCode, boundary);
            setLoading(false);
          }}
          disabled={loading || !slug || !name}
          className="btn-primary"
        >
          {loading ? "Starting…" : "Start Ingestion"}
        </button>
      </div>
    </div>
  );
}

// ─── Progress step ────────────────────────────────────────────────────────────



// ─── Main page ────────────────────────────────────────────────────────────────

export default function AddCityPage() {
  const [step, setStep] = useState<Step>("browse");
  const [selected, setSelected] = useState<GeofabrikFeature | null>(null);
  const [jobId, setJobId] = useState<string | null>(null);
  const [ingestError, setIngestError] = useState<string | null>(null);

  const handleConfirm = async (
    slug: string,
    name: string,
    countryCode: string,
    boundary: { type: string; coordinates: unknown } | null,
  ) => {
    setIngestError(null);
    try {
      const res = await fetch("/api/admin/cities", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          slug,
          name,
          countryCode,
          geofabrikUrl: selected!.properties.urls.pbf,
          ...(boundary ? { boundary } : {}),
        }),
      });
      if (!res.ok) {
        const data = await res.json();
        setIngestError(data.error ?? "Failed to start ingestion");
        return;
      }
      const data = await res.json();
      setJobId(data.jobId);
      setStep("ingest");
    } catch (e) {
      setIngestError(String(e));
    }
  };

  return (
    <div className="max-w-3xl mx-auto">
      <h1 className="text-2xl font-bold text-gray-900 mb-2">Add City</h1>
      <p className="text-sm text-gray-500 mb-6">
        Select an OpenStreetMap region to import for 15-minute city analysis.
      </p>

      <StepIndicator current={step} />

      {ingestError && (
        <div className="mb-4 p-3 bg-red-50 rounded text-sm text-red-700">
          {ingestError}
        </div>
      )}

      {step === "browse" && (
        <GeofabrikBrowser
          onSelect={(f) => {
            setSelected(f);
            setStep("confirm");
          }}
        />
      )}
      {step === "confirm" && selected && (
        <ConfirmStep
          region={selected}
          onBack={() => setStep("browse")}
          onConfirm={handleConfirm}
        />
      )}
      {step === "ingest" && <CityIngestProgress jobId={jobId} />}
    </div>
  );
}
