import { useEffect, useRef, useState } from "react";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import {
  ArrowClockwise,
  BatteryMedium,
  Clock,
  FlagCheckered,
  MapPin,
  NavigationArrow,
  Path,
  Speedometer
} from "@phosphor-icons/react";
import { getTrip, getTrips, type TripDetail, type TripSummary } from "./api";

function formatDistance(value: number): string {
  return value < 1 ? `${Math.round(value * 1_000)} m` : `${value.toFixed(1)} km`;
}

function formatDuration(seconds: number): string {
  const minutes = Math.max(0, Math.round(seconds / 60));
  const hours = Math.floor(minutes / 60);
  return hours ? `${hours}h ${minutes % 60}m` : `${minutes} min`;
}

function formatDate(timestamp: number): string {
  return new Intl.DateTimeFormat(undefined, {
    weekday: "short",
    day: "numeric",
    month: "short",
    hour: "2-digit",
    minute: "2-digit"
  }).format(timestamp);
}

function value(value_: number | null, suffix: string, digits = 0): string {
  return value_ === null ? "Unavailable" : `${value_.toFixed(digits)}${suffix}`;
}

export function TripsPage() {
  const [trips, setTrips] = useState<TripSummary[] | null>(null);
  const [selectedId, setSelectedId] = useState<number | null>(null);
  const [detail, setDetail] = useState<TripDetail | null>(null);
  const [error, setError] = useState("");
  const [detailError, setDetailError] = useState("");
  const [refreshing, setRefreshing] = useState(false);

  async function refresh() {
    setRefreshing(true);
    try {
      const nextTrips = await getTrips();
      setTrips(nextTrips);
      setError("");
      if (selectedId !== null && !nextTrips.some((trip) => trip.id === selectedId)) {
        setSelectedId(null);
        setDetail(null);
      }
    } catch (caught) {
      setError(caught instanceof Error ? caught.message : "Trips could not be loaded");
    } finally {
      setRefreshing(false);
    }
  }

  useEffect(() => { void refresh(); }, []);

  useEffect(() => {
    if (selectedId === null) return;
    setDetail(null);
    setDetailError("");
    void getTrip(selectedId)
      .then(setDetail)
      .catch((caught) => setDetailError(caught instanceof Error ? caught.message : "Trip details could not be loaded"));
  }, [selectedId]);

  return (
    <section className="trips-page">
      <div className="trips-toolbar">
        <div>
          <strong>{trips?.length ?? 0} recorded trips</strong>
          <span>Trips start above 5 km/h and close after three stationary minutes.</span>
        </div>
        <button className="secondary-button trips-refresh" onClick={() => void refresh()} disabled={refreshing}>
          <ArrowClockwise size={21} className={refreshing ? "spin" : ""} /> Refresh
        </button>
      </div>

      {error && <div className="status-error" role="alert">{error}<button onClick={() => void refresh()}>Try again</button></div>}
      {trips === null && !error ? <TripsSkeleton /> : trips?.length === 0 ? <EmptyTrips /> : (
        <div className="trips-workspace">
          <div className="trip-list" aria-label="Recorded trips">
            {trips?.map((trip) => (
              <button
                key={trip.id}
                className={`trip-row ${selectedId === trip.id ? "selected" : ""}`}
                onClick={() => setSelectedId(trip.id)}
                aria-pressed={selectedId === trip.id}
              >
                <span className="trip-row-route"><NavigationArrow size={20} weight="fill" /></span>
                <span className="trip-row-main">
                  <strong>{formatDate(trip.startedAt)}</strong>
                  <small>{trip.active ? "Recording now" : `${formatDuration(trip.durationSeconds)} · ${trip.deviceName}`}</small>
                </span>
                <span className="trip-row-distance">{formatDistance(trip.distanceKm)}</span>
                <span className="trip-row-speed">{value(trip.peakSpeedKph, " km/h")}</span>
              </button>
            ))}
          </div>

          <div className="trip-detail">
            {detailError ? <div className="trip-detail-state" role="alert"><MapPin size={34} />{detailError}</div>
              : selectedId === null ? <div className="trip-detail-state"><Path size={42} /><strong>Select a trip</strong><span>Open a drive to inspect its route, statistics, and notable points.</span></div>
                : detail === null ? <TripDetailSkeleton /> : <TripReport trip={detail} />}
          </div>
        </div>
      )}
    </section>
  );
}

function TripReport({ trip }: { trip: TripDetail }) {
  return (
    <article className="trip-report">
      <div className="trip-report-heading">
        <div>
          <span>{trip.active ? "Live trip" : "Trip report"}</span>
          <h2>{formatDate(trip.startedAt)}</h2>
          <p>{formatDuration(trip.durationSeconds)} recorded by {trip.deviceName}</p>
        </div>
        {trip.active && <span className="trip-live"><i /> Recording</span>}
      </div>
      <TripMap trip={trip} />
      <div className="trip-stat-grid">
        <TripStat icon={<Path size={22} />} label="Distance" value={formatDistance(trip.distanceKm)} />
        <TripStat icon={<Speedometer size={22} />} label="Peak speed" value={value(trip.peakSpeedKph, " km/h")} />
        <TripStat icon={<NavigationArrow size={22} />} label="Average speed" value={value(trip.averageSpeedKph, " km/h")} />
        <TripStat icon={<Clock size={22} />} label="Duration" value={formatDuration(trip.durationSeconds)} />
        <TripStat icon={<BatteryMedium size={22} />} label="Vehicle battery used" value={value(trip.batteryConsumedPercent, "%", 1)} />
        <TripStat icon={<BatteryMedium size={22} />} label="Energy used" value={value(trip.energyConsumedKwh, " kWh", 2)} />
      </div>
      <div className="trip-report-footer">
        <span><MapPin size={18} /> {trip.pointCount.toLocaleString()} GPS samples</span>
        <span><FlagCheckered size={18} /> {trip.endedAt ? `Ended ${formatDate(trip.endedAt)}` : "Trip in progress"}</span>
      </div>
    </article>
  );
}

function TripStat({ icon, label, value: displayValue }: { icon: React.ReactNode; label: string; value: string }) {
  return <div className="trip-stat"><span>{icon}{label}</span><strong>{displayValue}</strong></div>;
}

function TripMap({ trip }: { trip: TripDetail }) {
  const host = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!host.current || trip.points.length === 0) return;
    const map = L.map(host.current, { zoomControl: true, attributionControl: true });
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      maxZoom: 19,
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);
    const coordinates = trip.points.map((point) => L.latLng(point.latitude, point.longitude));
    L.polyline(coordinates, { color: "#0b1005", weight: 9, opacity: 0.72, lineCap: "round" }).addTo(map);
    L.polyline(coordinates, { color: "#b9e769", weight: 5, opacity: 0.95, lineCap: "round" }).addTo(map);
    const bounds = L.latLngBounds(coordinates);
    if (bounds.isValid() && coordinates.length > 1) map.fitBounds(bounds, { padding: [34, 34], maxZoom: 16 });
    else map.setView(coordinates[0]!, 16);

    for (const pin of trip.pins) {
      const peak = pin.type === "peak-speed";
      const marker = L.circleMarker([pin.latitude, pin.longitude], {
        radius: peak ? 9 : 7,
        color: peak ? "#10130f" : "#f0f2eb",
        weight: 3,
        fillColor: peak ? "#b9e769" : pin.type === "start" ? "#f0f2eb" : "#8f9a88",
        fillOpacity: 1
      }).addTo(map);
      marker.bindTooltip(peak ? `Top speed · ${value(pin.speedKph ?? null, " km/h")}` : pin.type === "start" ? "Trip start" : "Trip end");
    }
    return () => { map.remove(); };
  }, [trip]);

  if (trip.points.length === 0) return <div className="trip-map-empty"><MapPin size={34} />No route points were retained for this trip.</div>;
  return <div className="trip-map" ref={host} aria-label="Map showing the trip route" />;
}

function EmptyTrips() {
  return <div className="trips-empty"><Path size={46} /><h2>No trips recorded yet</h2><p>Pair the Android companion and leave its foreground service running. Your first drive will appear here automatically.</p></div>;
}

function TripsSkeleton() {
  return <div className="trips-skeleton" aria-label="Loading trips"><div /><div /><div /></div>;
}

function TripDetailSkeleton() {
  return <div className="trip-detail-skeleton" aria-label="Loading trip report"><div /><div /><div /></div>;
}
