import { Camera, GeoJSONSource, Layer, Map } from '@maplibre/maplibre-react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
import { ActivityIndicator, Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import Svg, { Defs, LinearGradient, Path, Stop } from 'react-native-svg';
import { useActivity, useSetting } from '@/db/queries';

const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';

// ── Types ────────────────────────────────────────────────────────────────────

type Timeseries = {
  t: number[];
  elevation_m: (number | null)[];
  lat?: (number | null)[] | null;
  lon?: (number | null)[] | null;
};

// ── Screen ───────────────────────────────────────────────────────────────────

export default function ActivityScreen() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const router = useRouter();
  const row = useActivity(id);
  const instanceUrl = useSetting('instance_url')?.replace(/\/$/, '') ?? '';
  const token = useSetting('api_token') ?? '';

  const [geojson, setGeojson] = useState<object | null>(null);
  const [timeseries, setTimeseries] = useState<Timeseries | null>(null);
  const [loadingMap, setLoadingMap] = useState(false);
  const [loadingChart, setLoadingChart] = useState(false);

  useEffect(() => {
    if (!row) return;

    if (row.geojson) {
      setGeojson(JSON.parse(row.geojson));
    } else if (row.origin === 'remote' && instanceUrl && token) {
      setLoadingMap(true);
      fetch(`${instanceUrl}/api/activity/${row.id}/geojson`, {
        headers: { Authorization: `Bearer ${token}` },
      })
        .then(r => r.ok ? r.json() : null)
        .then(data => { if (data) setGeojson(data); })
        .catch(() => {})
        .finally(() => setLoadingMap(false));
    }

    if (row.timeseries_json) {
      setTimeseries(JSON.parse(row.timeseries_json));
    } else if (row.origin === 'remote' && instanceUrl && token) {
      setLoadingChart(true);
      fetch(`${instanceUrl}/api/activity/${row.id}/timeseries`, {
        headers: { Authorization: `Bearer ${token}` },
      })
        .then(r => r.ok ? r.json() : null)
        .then(data => { if (data) setTimeseries(data); })
        .catch(() => {})
        .finally(() => setLoadingChart(false));
    }
  }, [row?.id]);

  if (!row) {
    return (
      <View style={styles.center}>
        <Text style={styles.notFound}>Activity not found</Text>
      </View>
    );
  }

  const detail = JSON.parse(row.detail_json);
  const km          = detail.distance_m != null ? (detail.distance_m / 1000).toFixed(2) : null;
  const elev        = detail.elevation_gain_m != null ? Math.round(detail.elevation_gain_m) : null;
  const elevLoss    = detail.elevation_loss_m != null ? Math.round(Math.abs(detail.elevation_loss_m)) : null;
  const movingTime  = detail.moving_time_s != null ? formatDuration(detail.moving_time_s) : null;
  const speed       = detail.avg_speed_kmh != null ? detail.avg_speed_kmh.toFixed(1) : null;
  const hr          = detail.avg_hr_bpm != null ? Math.round(detail.avg_hr_bpm) : null;
  const power       = detail.avg_power_w != null ? Math.round(detail.avg_power_w) : null;
  const date        = new Date(detail.started_at).toLocaleDateString(undefined, {
    weekday: 'long', day: 'numeric', month: 'long', year: 'numeric',
  });

  return (
    <ScrollView style={styles.container} contentContainerStyle={styles.content}>
      <Pressable style={styles.backButton} onPress={() => router.back()}>
        <Text style={styles.backText}>← Back</Text>
      </Pressable>

      <Text style={styles.sport}>{detail.sport ?? 'Activity'}</Text>
      <Text style={styles.title}>{detail.title}</Text>
      <Text style={styles.date}>{date}</Text>

      {/* Map */}
      <RouteMap geojson={geojson} loading={loadingMap} />

      {/* Stats grid */}
      <View style={styles.grid}>
        {km          && <StatCell label="Distance"       value={km}          unit="km"  />}
        {movingTime  && <StatCell label="Moving time"    value={movingTime}  unit=""    />}
        {elev   != null && <StatCell label="Elev gain"   value={String(elev)}     unit="m" />}
        {elevLoss != null && <StatCell label="Elev loss" value={String(elevLoss)} unit="m" />}
        {speed       && <StatCell label="Avg speed"      value={speed}       unit="km/h"/>}
        {hr          && <StatCell label="Avg HR"         value={String(hr)}  unit="bpm" />}
        {power       && <StatCell label="Avg power"      value={String(power)} unit="W" />}
      </View>

      {/* Elevation chart */}
      <ElevationChart timeseries={timeseries} loading={loadingChart} />

      {/* Meta */}
      <View style={styles.meta}>
        <MetaRow label="Source"  value={detail.source ?? '—'} />
        <MetaRow label="Device"  value={detail.device ?? '—'} />
        <MetaRow label="Origin"  value={row.origin} />
        <MetaRow label="Synced"  value={row.synced_at ? new Date(row.synced_at * 1000).toLocaleDateString() : 'No'} />
      </View>
    </ScrollView>
  );
}

// ── Map ───────────────────────────────────────────────────────────────────────

function RouteMap({ geojson, loading }: { geojson: object | null; loading: boolean }) {
  const [fullscreen, setFullscreen] = useState(false);

  if (loading) {
    return (
      <View style={styles.mapPlaceholder}>
        <ActivityIndicator color="#60a5fa" />
      </View>
    );
  }
  if (!geojson) return null;

  const bounds = geoJsonBounds(geojson);
  const routeSource = (
    <GeoJSONSource id="route" data={geojson as GeoJSON.FeatureCollection}>
      <Layer
        type="line"
        id="route-line"
        paint={{ 'line-color': '#60a5fa', 'line-width': 3 }}
        layout={{ 'line-cap': 'round', 'line-join': 'round' }}
      />
    </GeoJSONSource>
  );
  const camera = bounds ? (
    <Camera
      initialViewState={{
        bounds,
        padding: { top: 24, bottom: 24, left: 24, right: 24 },
      }}
    />
  ) : null;

  return (
    <>
      {/* Thumbnail — tap to expand */}
      <Pressable style={styles.mapContainer} onPress={() => setFullscreen(true)}>
        <Map
          style={styles.map}
          mapStyle={MAP_STYLE}
          dragPan={false}
          touchZoom={false}
          touchPitch={false}
          touchRotate={false}
        >
          {camera}
          {routeSource}
        </Map>
        <View style={styles.mapExpandHint}>
          <Text style={styles.mapExpandText}>⤢ tap to explore</Text>
        </View>
      </Pressable>

      {/* Full-screen interactive map */}
      <Modal visible={fullscreen} animationType="slide" onRequestClose={() => setFullscreen(false)}>
        <View style={styles.fullscreenMap}>
          <Map style={styles.map} mapStyle={MAP_STYLE}>
            {camera}
            {routeSource}
          </Map>
          <Pressable style={styles.closeButton} onPress={() => setFullscreen(false)}>
            <Text style={styles.closeText}>✕</Text>
          </Pressable>
        </View>
      </Modal>
    </>
  );
}

// ── Elevation chart ───────────────────────────────────────────────────────────

function ElevationChart({ timeseries, loading }: { timeseries: Timeseries | null; loading: boolean }) {
  const W = 340;
  const H = 100;
  const PAD = 4;

  if (loading) {
    return (
      <View style={styles.chartPlaceholder}>
        <ActivityIndicator color="#60a5fa" />
      </View>
    );
  }
  if (!timeseries) return null;

  const raw = timeseries.elevation_m;
  if (!raw || raw.length < 2) return null;

  // Downsample to ≤300 points
  const step  = Math.max(1, Math.floor(raw.length / 300));
  const times = timeseries.t.filter((_, i) => i % step === 0);
  const eles  = raw.filter((_, i) => i % step === 0).map(v => v ?? 0);

  const minE  = Math.min(...eles);
  const maxE  = Math.max(...eles);
  const range = maxE - minE || 1;
  const maxT  = times[times.length - 1] || 1;

  const x = (t: number) => PAD + (t / maxT) * (W - PAD * 2);
  const y = (e: number) => PAD + (1 - (e - minE) / range) * (H - PAD * 2);

  const pts = times.map((t, i) => `${x(t).toFixed(1)},${y(eles[i]).toFixed(1)}`);
  const linePath  = `M ${pts.join(' L ')}`;
  const areaPath  = `M ${x(times[0])},${H} L ${pts.join(' L ')} L ${x(maxT)},${H} Z`;

  return (
    <View style={styles.chartContainer}>
      <Text style={styles.chartLabel}>{Math.round(maxE)} m</Text>
      <Svg width={W} height={H} viewBox={`0 0 ${W} ${H}`}>
        <Defs>
          <LinearGradient id="grad" x1="0" y1="0" x2="0" y2="1">
            <Stop offset="0" stopColor="#60a5fa" stopOpacity="0.35" />
            <Stop offset="1" stopColor="#60a5fa" stopOpacity="0.02" />
          </LinearGradient>
        </Defs>
        <Path d={areaPath} fill="url(#grad)" />
        <Path d={linePath}  fill="none" stroke="#60a5fa" strokeWidth="1.5" strokeLinejoin="round" />
      </Svg>
      <Text style={styles.chartLabel}>{Math.round(minE)} m</Text>
    </View>
  );
}

// ── Helpers ───────────────────────────────────────────────────────────────────

// Returns [west, south, east, north] per LngLatBounds spec
function geoJsonBounds(gj: object): [number, number, number, number] | null {
  const coords: [number, number][] = [];
  function collect(obj: unknown) {
    if (!obj || typeof obj !== 'object') return;
    const o = obj as Record<string, unknown>;
    if (o.type === 'Feature') { collect(o.geometry); return; }
    if (o.type === 'FeatureCollection') { (o.features as unknown[]).forEach(collect); return; }
    if (o.type === 'LineString') { coords.push(...(o.coordinates as [number, number][])); return; }
    if (o.type === 'MultiLineString') { (o.coordinates as [number, number][][]).forEach(c => coords.push(...c)); return; }
  }
  collect(gj);
  if (!coords.length) return null;
  const lons = coords.map(c => c[0]);
  const lats = coords.map(c => c[1]);
  return [Math.min(...lons), Math.min(...lats), Math.max(...lons), Math.max(...lats)];
}

function formatDuration(seconds: number): string {
  const h = Math.floor(seconds / 3600);
  const m = Math.floor((seconds % 3600) / 60);
  const s = seconds % 60;
  if (h > 0) return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
  return `${m}:${String(s).padStart(2, '0')}`;
}

function StatCell({ label, value, unit }: { label: string; value: string; unit: string }) {
  return (
    <View style={styles.statCell}>
      <View style={styles.statValueRow}>
        <Text style={styles.statValue}>{value}</Text>
        {unit ? <Text style={styles.statUnit}>{unit}</Text> : null}
      </View>
      <Text style={styles.statLabel}>{label}</Text>
    </View>
  );
}

function MetaRow({ label, value }: { label: string; value: string }) {
  return (
    <View style={styles.metaRow}>
      <Text style={styles.metaLabel}>{label}</Text>
      <Text style={styles.metaValue}>{value}</Text>
    </View>
  );
}

// ── Styles ────────────────────────────────────────────────────────────────────

const styles = StyleSheet.create({
  container:      { flex: 1, backgroundColor: '#09090b' },
  content:        { paddingBottom: 40 },
  center:         { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: '#09090b' },
  notFound:       { color: '#71717a', fontSize: 16 },
  backButton:     { paddingHorizontal: 16, paddingTop: 60, paddingBottom: 12 },
  backText:       { color: '#60a5fa', fontSize: 15 },
  sport:          { color: '#71717a', fontSize: 12, fontWeight: '600', letterSpacing: 0.8, paddingHorizontal: 16, marginBottom: 4 },
  title:          { color: '#f4f4f5', fontSize: 22, fontWeight: '700', paddingHorizontal: 16, marginBottom: 4 },
  date:           { color: '#71717a', fontSize: 13, paddingHorizontal: 16, marginBottom: 16 },
  mapContainer:    { height: 220, marginBottom: 16, borderTopWidth: 1, borderBottomWidth: 1, borderColor: '#27272a' },
  map:             { flex: 1 },
  mapPlaceholder:  { height: 220, backgroundColor: '#18181b', alignItems: 'center', justifyContent: 'center', borderTopWidth: 1, borderBottomWidth: 1, borderColor: '#27272a', marginBottom: 16 },
  mapExpandHint:   { position: 'absolute', bottom: 8, right: 8, backgroundColor: 'rgba(0,0,0,0.55)', borderRadius: 6, paddingHorizontal: 8, paddingVertical: 4 },
  mapExpandText:   { color: '#a1a1aa', fontSize: 11 },
  fullscreenMap:   { flex: 1, backgroundColor: '#09090b' },
  closeButton:     { position: 'absolute', top: 56, right: 16, backgroundColor: 'rgba(0,0,0,0.6)', borderRadius: 20, width: 36, height: 36, alignItems: 'center', justifyContent: 'center' },
  closeText:       { color: '#fff', fontSize: 16 },
  chartContainer: { marginHorizontal: 16, marginBottom: 16, backgroundColor: '#18181b', borderRadius: 10, borderWidth: 1, borderColor: '#27272a', padding: 12, alignItems: 'flex-start' },
  chartPlaceholder: { height: 120, backgroundColor: '#18181b', alignItems: 'center', justifyContent: 'center', borderRadius: 10, borderWidth: 1, borderColor: '#27272a', marginHorizontal: 16, marginBottom: 16 },
  chartLabel:     { color: '#3f3f46', fontSize: 10, marginBottom: 2 },
  grid:           { flexDirection: 'row', flexWrap: 'wrap', paddingHorizontal: 12, gap: 8, marginBottom: 16 },
  statCell:       { backgroundColor: '#18181b', borderRadius: 10, borderWidth: 1, borderColor: '#27272a', padding: 14, width: '47%' },
  statValueRow:   { flexDirection: 'row', alignItems: 'baseline', gap: 4, marginBottom: 4 },
  statValue:      { color: '#f4f4f5', fontSize: 24, fontWeight: '700' },
  statUnit:       { color: '#71717a', fontSize: 13 },
  statLabel:      { color: '#71717a', fontSize: 12 },
  meta:           { marginHorizontal: 16, backgroundColor: '#18181b', borderRadius: 10, borderWidth: 1, borderColor: '#27272a' },
  metaRow:        { flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 14, paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#27272a' },
  metaLabel:      { color: '#71717a', fontSize: 13 },
  metaValue:      { color: '#a1a1aa', fontSize: 13 },
});
