import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
  ActivityIndicator,
  Alert,
  FlatList,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import type { RouteProp } from '@react-navigation/native';
import { useNavigation, useRoute } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { ps } from '@/api/client';
import { useTheme } from '@/theme/ThemeContext';
import { scheduleExpiryReminder } from '@/notifications/localReminders';
import { logLine } from '@/features/diagnostics/fileLogger';
import type { RootStackParamList } from '@/navigation/RootNavigator';
import type {
  CreditCardDetail,
  ParkingDetail,
  VehicleDetail,
} from 'parksmarter-client';

type SessionRoute = RouteProp<RootStackParamList, 'StartSession'>;
type Nav = NativeStackNavigationProp<RootStackParamList>;

function fmtDuration(min?: number): string {
  if (!min) return '';
  const h = Math.floor(min / 60);
  const m = min % 60;
  return h ? (m ? `${h}h ${m}m` : `${h}h`) : `${m}m`;
}
const money = (v?: string | number) => `$${Number(v ?? 0).toFixed(2)}`;

/**
 * Some zones (e.g. short-term "DL" spaces) reject ParkingEstimateMulti with
 * "Unable to process your request" even though the single-duration estimate
 * works. Build a price ladder from ParkingEstimate (single) across the zone's
 * Min/Max time so those zones stay purchasable instead of showing a blank screen.
 */
async function buildSingleLadder(base: {
  zoneId: number;
  spaceId: number;
  customerId: number;
  vehicleId: number;
}): Promise<ParkingDetail[]> {
  const probe = await ps.getParkingEstimateSingle({ ...base, durationInMinutes: 30, creditCardId: 0 });
  if ((probe as any)?.Response?.Status === 'Error') {
    throw new Error((probe as any).Response?.Message || 'Estimate unavailable for this space.');
  }
  const min = Math.max(5, Number(probe.MinTime) || 5);
  const max = Math.max(min, Number(probe.MaxTime) || min);
  const span = max - min;
  const step = span > 180 ? 30 : span > 60 ? 15 : span > 20 ? 10 : 5;
  const durs = new Set<number>();
  for (let d = min; d < max; d += step) durs.add(d);
  durs.add(max);
  const rungs = await Promise.all(
    [...durs].map(async (d) => {
      try {
        const r = await ps.getParkingEstimateSingle({ ...base, durationInMinutes: d, creditCardId: 0 });
        const p = r.ParkingDetail;
        if (!p || (r as any)?.Response?.Status === 'Error') return null;
        // The single endpoint often echoes Minutes:0; trust the requested duration.
        return { ...p, Minutes: d } as ParkingDetail;
      } catch {
        return null;
      }
    }),
  );
  return rungs.filter((r): r is ParkingDetail => r != null);
}

/** Parse the API's "MM-DD-YYYY hh:mm AM" end-time string into a Date for reminders. */
function parseApiTime(s?: string): Date | null {
  if (!s) return null;
  const m = s.match(/(\d{2})-(\d{2})-(\d{4})\s+(\d{1,2}):(\d{2})\s*(AM|PM)/i);
  if (!m) return null;
  let hr = parseInt(m[4], 10);
  const pm = /pm/i.test(m[6]);
  if (pm && hr !== 12) hr += 12;
  if (!pm && hr === 12) hr = 0;
  return new Date(+m[3], +m[1] - 1, +m[2], hr, +m[5]);
}

export function StartSessionScreen() {
  const { colors } = useTheme();
  const navigation = useNavigation<Nav>();
  const { zone } = useRoute<SessionRoute>().params;
  const space = zone.Spaces?.[0];

  // The policy in effect right now (CurrentSlot). Drives whether paid parking is
  // even applicable — some windows are Free or No Parking (per the meter's schedule).
  const currentPolicy = space?.Policies?.find((p) => p.CurrentSlot);
  const currentType = (currentPolicy?.RateType ?? '').toLowerCase();
  const currentlyFree = currentType.includes('free');
  const noParkingNow = currentType.includes('no parking');

  const [vehicles, setVehicles] = useState<VehicleDetail[]>([]);
  const [cards, setCards] = useState<CreditCardDetail[]>([]);
  const [vehicleId, setVehicleId] = useState<number | undefined>();
  const [cardId, setCardId] = useState<number | undefined>();
  const [ladder, setLadder] = useState<ParkingDetail[]>([]);
  const [selIdx, setSelIdx] = useState(0);
  const [loading, setLoading] = useState(true);
  const [paying, setPaying] = useState(false);
  const [ladderError, setLadderError] = useState<string | null>(null);

  // Load the account's vehicles + cards and pick the defaults.
  useEffect(() => {
    ps.getUserDetail()
      .then((u) => {
        const vs = u.VehicleDetails ?? [];
        const cs = u.CreditCardDetails ?? [];
        setVehicles(vs);
        setCards(cs);
        const defV = vs.find((v) => String(v.IsDefault) === 'true') ?? vs[0];
        const defC = cs.find((c) => String(c.CCDefault) === 'true') ?? cs[0];
        setVehicleId(defV?.VehicleID as number | undefined);
        setCardId(defC?.CCID as number | undefined);
      })
      .catch(() => {});
  }, []);

  // (Re)fetch the price ladder whenever the vehicle changes — rates can vary by vehicle.
  const loadLadder = useCallback(async () => {
    if (vehicleId == null || space?.SpaceId == null) return;
    setLoading(true);
    setLadderError(null);
    const base = {
      zoneId: zone.ZoneId!,
      spaceId: space.SpaceId!,
      customerId: zone.CustomerId!,
      vehicleId,
    };
    try {
      let details: ParkingDetail[] = [];
      let multiErrored = false;
      try {
        const est = await ps.getParkingEstimateMulti(base);
        if ((est as any)?.Response?.Status === 'Error') multiErrored = true;
        else details = est.ParkingDetail ?? [];
      } catch {
        multiErrored = true;
      }
      // Multi is unsupported or empty for some zones — fall back to single estimates.
      if (!details.length) {
        logLine(
          `[ESTIMATE] multi ${multiErrored ? 'errored' : 'empty'} for zone=${base.zoneId} ` +
            `space=${base.spaceId}; falling back to single`,
        );
        details = await buildSingleLadder(base);
      }
      if (!details.length) {
        throw new Error('No parking options are available for this space right now.');
      }
      setLadder(details);
      setSelIdx((i) => Math.min(i, Math.max(0, details.length - 1)));
    } catch (e: any) {
      const msg = e?.serverMessage ?? e?.message ?? 'Could not load parking options.';
      setLadder([]);
      setLadderError(msg);
      logLine(`[ESTIMATE] failed zone=${base.zoneId} space=${base.spaceId}: ${msg}`);
    } finally {
      setLoading(false);
    }
  }, [vehicleId, space?.SpaceId, zone.ZoneId, zone.CustomerId]);

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

  const selected = ladder[selIdx];

  // Detect a free prefix (e.g., inside a "Free until 6:15 AM" policy window).
  const freeInfo = useMemo(() => {
    const firstPaid = ladder.findIndex((d) => Number(d.ParkingCost) > 0);
    if (firstPaid > 0) {
      return { until: ladder[firstPaid - 1]?.EndTime, minutes: ladder[firstPaid - 1]?.Minutes };
    }
    return null;
  }, [ladder]);

  const total = selected
    ? Number(selected.ParkingCost ?? 0) + Number(selected.TransactionFee ?? 0)
    : 0;

  const onPay = () => {
    if (!selected || cardId == null || vehicleId == null) {
      Alert.alert('Missing info', 'Pick a vehicle, duration, and card first.');
      return;
    }
    Alert.alert(
      'Confirm payment',
      `Park at ${zone.ZoneName} for ${fmtDuration(selected.Minutes)}.\n\n` +
        `Parking ${money(selected.ParkingCost)}` +
        (Number(selected.TransactionFee) > 0 ? ` + fee ${money(selected.TransactionFee)}` : '') +
        `\nTotal: ${money(total)}\n\nThis charges your card now.`,
      [
        { text: 'Cancel', style: 'cancel' },
        { text: `Pay ${money(total)}`, style: 'default', onPress: doStart },
      ],
    );
  };

  const doStart = async () => {
    if (!selected) return;
    setPaying(true);
    logLine(
      `[SESSION] start attempt zone=${zone.ZoneName} space=${space?.SpaceId} vehicle=${vehicleId} ` +
        `card=${cardId} min=${selected.Minutes} cost=${selected.ParkingCost} fee=${selected.TransactionFee} ` +
        `start="${selected.StartTime}" end="${selected.EndTime}"`,
    );
    try {
      const res = await ps.startParkingSession({
        creditCardId: cardId!,
        vehicleId: vehicleId!,
        zoneId: zone.ZoneId!,
        spaceId: space!.SpaceId!,
        customerId: zone.CustomerId!,
        startTime: selected.StartTime!,
        endTime: selected.EndTime!,
        minutesToPurchase: selected.Minutes!,
        parkingCost: Number(selected.ParkingCost ?? 0),
        transactionFee: Number(selected.TransactionFee ?? 0),
        minCreditAmount: zone.MinimumAmount,
        meterTypeId: zone.MeterTypeId!,
      });
      // Schedule the local expiry reminder from the purchased end time.
      const end = parseApiTime(selected.EndTime);
      if (end) {
        await scheduleExpiryReminder({
          transactionId: (res as any)?.TransactionID ?? Date.now(),
          zoneName: zone.ZoneName ?? 'Parking',
          endTime: end,
        });
      }
      logLine(`[SESSION] start OK: ${JSON.stringify(res)}`);
      Alert.alert('Parked!', `Session started at ${zone.ZoneName}.`, [
        { text: 'OK', onPress: () => navigation.navigate('Tabs') },
      ]);
    } catch (e: any) {
      logLine(`[SESSION] start FAILED: ${e?.status ?? ''} ${e?.serverMessage ?? e?.message ?? e}`);
      Alert.alert('Could not start session', e?.serverMessage ?? e?.message ?? 'error');
    } finally {
      setPaying(false);
    }
  };

  // When the current time-of-day policy is Free or No Parking, paid parking
  // doesn't apply — show the status instead of a purchase flow (matches the app).
  if (currentlyFree || noParkingNow) {
    return (
      <View style={[styles.center, { backgroundColor: colors.bg, padding: 24 }]}>
        <Text style={[styles.zone, { color: colors.text }]}>{zone.ZoneName}</Text>
        <View
          style={[
            styles.freeBanner,
            { backgroundColor: currentlyFree ? '#e8f5e9' : '#fdecea', marginTop: 16, alignSelf: 'stretch' },
          ]}
        >
          <Text style={{ color: currentlyFree ? '#2e7d32' : '#c0392b', fontWeight: '700', fontSize: 16 }}>
            {currentlyFree ? 'Parking is currently free' : 'No parking allowed right now'}
          </Text>
          {currentPolicy?.MessageText && currentPolicy.MessageText !== 'null' ? (
            <Text style={{ color: colors.subtext, marginTop: 6 }}>{currentPolicy.MessageText}</Text>
          ) : null}
          {currentPolicy?.DisplayString ? (
            <Text style={{ color: colors.subtext, marginTop: 4 }}>{currentPolicy.DisplayString}</Text>
          ) : null}
        </View>
      </View>
    );
  }

  if (loading && !ladder.length) {
    return (
      <View style={[styles.center, { backgroundColor: colors.bg }]}>
        <ActivityIndicator color={colors.primary} />
      </View>
    );
  }

  if (ladderError && !ladder.length) {
    return (
      <View style={[styles.center, { backgroundColor: colors.bg, padding: 24 }]}>
        <Text style={[styles.zone, { color: colors.text }]}>{zone.ZoneName}</Text>
        <View style={[styles.freeBanner, { backgroundColor: '#fdecea', marginTop: 16, alignSelf: 'stretch' }]}>
          <Text style={{ color: '#c0392b', fontWeight: '700', fontSize: 16 }}>
            Couldn’t load parking options
          </Text>
          <Text style={{ color: colors.subtext, marginTop: 6 }}>{ladderError}</Text>
        </View>
        <TouchableOpacity
          style={[styles.payBtn, { backgroundColor: colors.primary, marginTop: 20, alignSelf: 'stretch' }]}
          onPress={() => void loadLadder()}
        >
          <Text style={styles.payText}>Try again</Text>
        </TouchableOpacity>
      </View>
    );
  }

  const Chip = ({
    active,
    label,
    onPress,
  }: {
    active: boolean;
    label: string;
    onPress: () => void;
  }) => (
    <TouchableOpacity
      onPress={onPress}
      style={[
        styles.chip,
        { borderColor: active ? colors.primary : colors.border, backgroundColor: active ? colors.primary : colors.card },
      ]}
    >
      <Text style={{ color: active ? '#fff' : colors.text, fontWeight: '600' }}>{label}</Text>
    </TouchableOpacity>
  );

  return (
    <ScrollView style={{ backgroundColor: colors.bg }} contentContainerStyle={{ padding: 16 }}>
      <Text style={[styles.zone, { color: colors.text }]}>{zone.ZoneName}</Text>
      <Text style={[styles.sub, { color: colors.subtext }]}>
        {zone.ZoneLocation ?? space?.SpaceName ?? ''}
      </Text>

      <Text style={[styles.label, { color: colors.subtext }]}>Vehicle</Text>
      <View style={styles.chipRow}>
        {vehicles.map((v) => (
          <Chip
            key={String(v.VehicleID)}
            active={v.VehicleID === vehicleId}
            label={`${v.VehicleAlias || v.VehiclePlate} `}
            onPress={() => setVehicleId(v.VehicleID as number)}
          />
        ))}
      </View>

      <Text style={[styles.label, { color: colors.subtext }]}>Duration</Text>
      <FlatList
        horizontal
        data={ladder}
        keyExtractor={(d, i) => String(d.Minutes ?? i)}
        showsHorizontalScrollIndicator={false}
        renderItem={({ item, index }) => (
          <Chip
            active={index === selIdx}
            label={`${fmtDuration(item.Minutes)} · ${money(item.ParkingCost)}`}
            onPress={() => setSelIdx(index)}
          />
        )}
      />

      <Text style={[styles.label, { color: colors.subtext }]}>Card</Text>
      <View style={styles.chipRow}>
        {cards.map((c) => (
          <Chip
            key={String(c.CCID)}
            active={c.CCID === cardId}
            label={`${c.CCAlias || 'Card'} ••${c.CCLastFour ?? ''}`}
            onPress={() => setCardId(c.CCID as number)}
          />
        ))}
        {cards.length === 0 ? (
          <Text style={{ color: colors.subtext }}>No cards on file.</Text>
        ) : null}
      </View>

      {freeInfo ? (
        <View style={[styles.freeBanner, { backgroundColor: '#e8f5e9' }]}>
          <Text style={{ color: '#2e7d32', fontWeight: '600' }}>
            Free until {freeInfo.until} ({fmtDuration(freeInfo.minutes)}), then charged.
          </Text>
        </View>
      ) : null}

      {selected ? (
        <View style={[styles.quote, { backgroundColor: colors.card }]}>
          <Row label="Duration" value={fmtDuration(selected.Minutes)} colors={colors} />
          <Row label="Ends" value={selected.EndTime ?? ''} colors={colors} />
          <Row label="Parking" value={money(selected.ParkingCost)} colors={colors} />
          {Number(selected.TransactionFee) > 0 ? (
            <Row label="Fee" value={money(selected.TransactionFee)} colors={colors} />
          ) : null}
          <Row label="Total" value={money(total)} bold colors={colors} />
        </View>
      ) : null}

      <TouchableOpacity
        style={[styles.payBtn, { backgroundColor: colors.primary, opacity: paying ? 0.6 : 1 }]}
        disabled={paying || !selected}
        onPress={onPay}
      >
        <Text style={styles.payText}>
          {paying ? 'Starting…' : `Pay ${money(total)} & start`}
        </Text>
      </TouchableOpacity>
    </ScrollView>
  );
}

function Row({
  label,
  value,
  bold,
  colors,
}: {
  label: string;
  value: string;
  bold?: boolean;
  colors: { text: string; subtext: string };
}) {
  return (
    <View style={styles.quoteRow}>
      <Text style={{ color: colors.subtext }}>{label}</Text>
      <Text style={{ color: colors.text, fontWeight: bold ? '800' : '600', fontSize: bold ? 18 : 15 }}>
        {value}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  center: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  zone: { fontSize: 24, fontWeight: '700' },
  sub: { marginBottom: 8 },
  label: { fontSize: 13, fontWeight: '600', marginTop: 18, marginBottom: 8 },
  chipRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },
  chip: {
    borderWidth: 1.5,
    borderRadius: 20,
    paddingHorizontal: 14,
    paddingVertical: 9,
    marginRight: 8,
  },
  freeBanner: { borderRadius: 10, padding: 12, marginTop: 16 },
  quote: { borderRadius: 12, padding: 16, marginTop: 16 },
  quoteRow: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 5 },
  payBtn: { borderRadius: 12, padding: 18, alignItems: 'center', marginTop: 20 },
  payText: { color: '#fff', fontWeight: '800', fontSize: 17 },
});
