/*
 * F-C2/F-C3 [모바일] 관람객(B2C) 현장 — 실시간 혼잡 + 주차 현황 + 사전 주차권(mock).
 * 세그먼트: 혼잡도 | 주차. 혼잡/주차 현황은 공개 조회, 주차권은 인증(본인).
 * 계약: _workspace/parking_congestion_contract.md. PII(차량번호)는 서버 마스킹 — 원문 미저장.
 */
import { Ionicons } from '@expo/vector-icons';
import { router } from 'expo-router';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
  ActivityIndicator,
  Modal,
  Pressable,
  ScrollView,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';
import { Banner } from '../../components/Banner';
import { CongestionPill } from '../../components/CongestionPill';
import { useAuth } from '../../context/AuthContext';
import { isDegraded } from '../../lib/api';
import { DEFAULT_PUBLIC_EVENT_ID } from '../../lib/config';
import type {
  CongestionAreaDto,
  CongestionOverviewDto,
  ParkingLotStatusDto,
  ParkingPassDto,
} from '../../lib/types';
import {
  getCongestion,
  getMyParkingPasses,
  getParkingLots,
  purchaseParkingPass,
} from '../../lib/visitor';
import { colors, radius, spacing, touch, type } from '../../theme';

type Seg = 'congestion' | 'parking';

function won(n: number): string {
  return `${n.toLocaleString('ko-KR')}`;
}
function ymd(d: Date): string {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

export default function VisitorOnsiteScreen() {
  const { t } = useTranslation();
  const { token, activeWorkspace } = useAuth();
  const eventId = activeWorkspace?.eventId ?? DEFAULT_PUBLIC_EVENT_ID;

  const [seg, setSeg] = useState<Seg>('congestion');

  return (
    <View style={styles.flex}>
      {/* 세그먼트 */}
      <View style={styles.segment}>
        {(['congestion', 'parking'] as Seg[]).map((s) => {
          const on = seg === s;
          return (
            <Pressable
              key={s}
              accessibilityRole="tab"
              accessibilityState={{ selected: on }}
              style={[styles.segBtn, on && styles.segBtnOn]}
              onPress={() => setSeg(s)}
            >
              <Text style={[styles.segText, on && styles.segTextOn]}>
                {t(s === 'congestion' ? 'onsite.segCongestion' : 'onsite.segParking')}
              </Text>
            </Pressable>
          );
        })}
      </View>

      {seg === 'congestion' ? (
        <CongestionView eventId={eventId} />
      ) : (
        <ParkingView eventId={eventId} hasToken={!!token} />
      )}
    </View>
  );
}

// ── 혼잡도 ──
function CongestionView({ eventId }: { eventId: string }) {
  const { t } = useTranslation();
  const [data, setData] = useState<CongestionOverviewDto | null>(null);
  const [loading, setLoading] = useState(true);
  const [degraded, setDegraded] = useState(false);

  const load = useCallback(async () => {
    try {
      const res = await getCongestion(eventId);
      setData(res);
      setDegraded(false);
    } catch (e) {
      setDegraded(isDegraded(e));
      setData(null);
    } finally {
      setLoading(false);
    }
  }, [eventId]);

  useEffect(() => {
    load();
  }, [load]);

  if (loading) {
    return (
      <View style={styles.loading}>
        <ActivityIndicator color={colors.primary600} size="large" />
      </View>
    );
  }
  if (!data) {
    return (
      <ScrollView contentContainerStyle={styles.scroll}>
        <Banner tone={degraded ? 'degraded' : 'info'}>
          {degraded ? t('congestion.degraded') : t('congestion.empty')}
        </Banner>
      </ScrollView>
    );
  }

  return (
    <ScrollView contentContainerStyle={styles.scroll}>
      {/* 종합 */}
      <View style={styles.overallCard}>
        <View style={styles.overallTop}>
          <Text style={styles.overallLabel}>{t('congestion.overall')}</Text>
          <CongestionPill level={data.overallLevel} label={data.overallLabel} />
        </View>
        <View style={styles.overallMetric}>
          <Ionicons name="people-outline" size={18} color={colors.neutral500} />
          <Text style={styles.overallCount}>
            {t('congestion.onSite')} {t('congestion.people', { n: won(data.onSiteCount) })}
          </Text>
        </View>
      </View>

      <AreaGroup title={t('congestion.gates')} icon="enter-outline" areas={data.entryGates} />
      <AreaGroup title={t('congestion.popular')} icon="star-outline" areas={data.popularSessions} />
      <AreaGroup title={t('congestion.parking')} icon="car-outline" areas={data.parking} />
    </ScrollView>
  );
}

function AreaGroup({
  title,
  icon,
  areas,
}: {
  title: string;
  icon: keyof typeof Ionicons.glyphMap;
  areas: CongestionAreaDto[];
}) {
  if (!areas || areas.length === 0) return null;
  return (
    <View style={styles.group}>
      <View style={styles.groupHeader}>
        <Ionicons name={icon} size={16} color={colors.primary600} />
        <Text style={styles.groupTitle}>{title}</Text>
      </View>
      {areas.map((a) => (
        <View key={a.id} style={styles.areaRow}>
          <Text style={styles.areaLabel} numberOfLines={1}>
            {a.label}
          </Text>
          <CongestionPill level={a.level} label={a.levelLabel} percent={a.occupancyPercent} size="sm" />
        </View>
      ))}
    </View>
  );
}

// ── 주차 ──
function ParkingView({ eventId, hasToken }: { eventId: string; hasToken: boolean }) {
  const { t } = useTranslation();
  const [lots, setLots] = useState<ParkingLotStatusDto[] | null>(null);
  const [degraded, setDegraded] = useState(false);
  const [passes, setPasses] = useState<ParkingPassDto[] | null>(null);
  const [purchaseFor, setPurchaseFor] = useState<ParkingLotStatusDto | null>(null);

  const loadLots = useCallback(async () => {
    try {
      const res = await getParkingLots(eventId);
      setLots(res ?? []);
      setDegraded(false);
    } catch (e) {
      setDegraded(isDegraded(e));
      setLots([]);
    }
  }, [eventId]);

  const loadPasses = useCallback(async () => {
    if (!hasToken) {
      setPasses(null);
      return;
    }
    try {
      const res = await getMyParkingPasses();
      setPasses(res?.passes ?? []);
    } catch {
      setPasses([]); // degraded/미배포 시 빈 목록(정직)
    }
  }, [hasToken]);

  useEffect(() => {
    loadLots();
    loadPasses();
  }, [loadLots, loadPasses]);

  return (
    <ScrollView contentContainerStyle={styles.scroll}>
      {/* 주차 현황 */}
      <View style={styles.groupHeader}>
        <Ionicons name="car-outline" size={16} color={colors.primary600} />
        <Text style={styles.groupTitle}>{t('parking.title')}</Text>
      </View>

      {lots == null ? (
        <View style={styles.loading}>
          <ActivityIndicator color={colors.primary600} />
        </View>
      ) : lots.length === 0 ? (
        <Banner tone={degraded ? 'degraded' : 'info'}>
          {degraded ? t('parking.degraded') : t('parking.empty')}
        </Banner>
      ) : (
        lots.map((lot) => (
          <LotCard key={lot.lotId} lot={lot} onBuy={() => setPurchaseFor(lot)} canBuy={hasToken} />
        ))
      )}

      {/* 내 주차권 */}
      <View style={[styles.groupHeader, { marginTop: spacing.sm }]}>
        <Ionicons name="pricetag-outline" size={16} color={colors.primary600} />
        <Text style={styles.groupTitle}>{t('parking.myPasses')}</Text>
      </View>
      {!hasToken ? (
        <Pressable style={styles.loginNote} onPress={() => router.push('/login')}>
          <Text style={styles.loginNoteText}>{t('parking.loginRequired')}</Text>
          <Ionicons name="chevron-forward" size={16} color={colors.primary600} />
        </Pressable>
      ) : passes == null ? (
        <View style={styles.loading}>
          <ActivityIndicator color={colors.primary600} />
        </View>
      ) : passes.length === 0 ? (
        <Text style={styles.emptyNote}>{t('parking.noPasses')}</Text>
      ) : (
        passes.map((p) => <PassCard key={p.passNo} pass={p} />)
      )}

      <PurchaseModal
        lot={purchaseFor}
        onClose={() => setPurchaseFor(null)}
        onPurchased={() => {
          setPurchaseFor(null);
          loadPasses();
          loadLots();
        }}
        eventId={eventId}
      />
    </ScrollView>
  );
}

function LotCard({
  lot,
  onBuy,
  canBuy,
}: {
  lot: ParkingLotStatusDto;
  onBuy: () => void;
  canBuy: boolean;
}) {
  const { t } = useTranslation();
  const pct = Math.max(0, Math.min(100, lot.occupancyPercent));
  return (
    <View style={styles.lotCard}>
      <View style={styles.lotTop}>
        <Text style={styles.lotName} numberOfLines={1}>
          {lot.name}
        </Text>
        <CongestionPill level={lot.congestionLevel} label={lot.congestionLabel} percent={pct} size="sm" />
      </View>
      {/* 점유율 바 */}
      <View style={styles.bar}>
        <View style={[styles.barFill, { width: `${pct}%` }]} />
      </View>
      <View style={styles.lotMetaRow}>
        <Text style={styles.lotMeta}>
          {t('parking.available', { n: won(lot.available) })} · {t('parking.capacity', { n: won(lot.totalCapacity) })}
        </Text>
      </View>
      <View style={styles.lotMetaRow}>
        <Text style={styles.lotMeta}>
          {t('parking.rate', { n: won(lot.hourlyRate) })}
          {lot.dailyMax != null ? ` · ${t('parking.daily', { n: won(lot.dailyMax) })}` : ''}
        </Text>
      </View>
      <Pressable
        accessibilityRole="button"
        style={[styles.buyBtn, !canBuy && styles.buyBtnGhost]}
        onPress={onBuy}
        disabled={!canBuy}
      >
        <Ionicons
          name="ticket-outline"
          size={16}
          color={canBuy ? colors.white : colors.neutral500}
        />
        <Text style={[styles.buyText, !canBuy && { color: colors.neutral500 }]}>
          {t('parking.buyPass')} · {t('parking.passPrice', { n: won(lot.passPrice) })}
        </Text>
      </Pressable>
      {!canBuy ? <Text style={styles.buyHint}>{t('parking.loginRequired')}</Text> : null}
    </View>
  );
}

const PASS_STATUS_TONE: Record<ParkingPassDto['status'], { color: string; bg: string }> = {
  PAID: { color: colors.success, bg: '#E7F6EF' },
  USED: { color: colors.neutral700, bg: colors.neutral050 },
  CANCELLED: { color: colors.error, bg: '#FEF3F2' },
};

function PassCard({ pass }: { pass: ParkingPassDto }) {
  const { t } = useTranslation();
  const tone = PASS_STATUS_TONE[pass.status] ?? PASS_STATUS_TONE.USED;
  const dim = pass.status !== 'PAID';
  return (
    <View style={[styles.passCard, dim && styles.passDim]}>
      <View style={styles.passTop}>
        <Text style={styles.passLot} numberOfLines={1}>
          {pass.lotName}
        </Text>
        <View style={[styles.passBadge, { backgroundColor: tone.bg }]}>
          <Text style={[styles.passBadgeText, { color: tone.color }]}>
            {t(`parking.passStatus.${pass.status}` as const)}
          </Text>
        </View>
      </View>
      <Text style={styles.passMeta}>
        {pass.useDate} · {pass.passNo}
      </Text>
      <View style={styles.passBottom}>
        <Text style={styles.passMeta}>
          {pass.vehiclePlateMasked ?? '-'}
        </Text>
        <Text style={styles.passAmount}>{t('parking.passPrice', { n: won(pass.amount) })}</Text>
      </View>
    </View>
  );
}

function PurchaseModal({
  lot,
  eventId,
  onClose,
  onPurchased,
}: {
  lot: ParkingLotStatusDto | null;
  eventId: string;
  onClose: () => void;
  onPurchased: () => void;
}) {
  const { t } = useTranslation();
  const [dayOffset, setDayOffset] = useState(1); // 내일 기본(오늘 이후)
  const [plate, setPlate] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState<string | null>(null);

  const useDate = useMemo(() => {
    const d = new Date();
    d.setDate(d.getDate() + dayOffset);
    return d;
  }, [dayOffset]);

  // 모달이 열릴 때마다 초기화
  useEffect(() => {
    if (lot) {
      setDayOffset(1);
      setPlate('');
      setErr(null);
      setBusy(false);
    }
  }, [lot]);

  async function submit() {
    if (!lot) return;
    setBusy(true);
    setErr(null);
    try {
      await purchaseParkingPass({
        lotId: lot.lotId,
        useDate: ymd(useDate),
        eventId,
        vehiclePlate: plate.trim() ? plate.trim() : undefined,
        payMethod: 'card',
      });
      onPurchased();
    } catch (e) {
      setErr((e as Error).message || t('parking.purchaseErr'));
      setBusy(false);
    }
  }

  return (
    <Modal visible={!!lot} transparent animationType="slide" onRequestClose={onClose}>
      <View style={styles.modalBackdrop}>
        <View style={styles.modalSheet}>
          <View style={styles.modalHeader}>
            <Text style={styles.modalTitle}>{t('parking.purchaseTitle')}</Text>
            <Pressable onPress={onClose} accessibilityLabel={t('common.close')} hitSlop={8}>
              <Ionicons name="close" size={22} color={colors.neutral500} />
            </Pressable>
          </View>

          {lot ? (
            <>
              {/* 선택 주차장 */}
              <Text style={styles.modalLabel}>{t('parking.selectLot')}</Text>
              <View style={styles.modalLotBox}>
                <Text style={styles.modalLotName}>{lot.name}</Text>
                <Text style={styles.modalLotPrice}>{t('parking.passPrice', { n: won(lot.passPrice) })}</Text>
              </View>

              {/* 이용일 스텝퍼 */}
              <Text style={styles.modalLabel}>{t('parking.useDate')}</Text>
              <View style={styles.dateRow}>
                <Pressable
                  style={styles.dateBtn}
                  disabled={dayOffset <= 1}
                  onPress={() => setDayOffset((v) => Math.max(1, v - 1))}
                  accessibilityLabel={t('parking.prevDay')}
                >
                  <Ionicons
                    name="chevron-back"
                    size={20}
                    color={dayOffset <= 1 ? colors.neutral200 : colors.neutral700}
                  />
                </Pressable>
                <Text style={styles.dateText}>{ymd(useDate)}</Text>
                <Pressable
                  style={styles.dateBtn}
                  disabled={dayOffset >= 14}
                  onPress={() => setDayOffset((v) => Math.min(14, v + 1))}
                  accessibilityLabel={t('parking.nextDay')}
                >
                  <Ionicons
                    name="chevron-forward"
                    size={20}
                    color={dayOffset >= 14 ? colors.neutral200 : colors.neutral700}
                  />
                </Pressable>
              </View>

              {/* 차량번호(선택) */}
              <Text style={styles.modalLabel}>{t('parking.plate')}</Text>
              <TextInput
                style={styles.input}
                value={plate}
                onChangeText={setPlate}
                placeholder={t('parking.platePlaceholder')}
                placeholderTextColor={colors.neutral500}
                autoCapitalize="none"
                autoCorrect={false}
              />
              <Text style={styles.modalNote}>{t('parking.plateNote')}</Text>
              <Text style={styles.modalNote}>{t('parking.payNote')}</Text>

              {err ? <Banner tone="error">{err}</Banner> : null}

              <Pressable
                style={[styles.confirmBtn, busy && styles.confirmBusy]}
                onPress={submit}
                disabled={busy}
                accessibilityRole="button"
              >
                {busy ? (
                  <ActivityIndicator color={colors.white} />
                ) : (
                  <Text style={styles.confirmText}>{t('parking.confirm')}</Text>
                )}
              </Pressable>
            </>
          ) : null}
        </View>
      </View>
    </Modal>
  );
}

const styles = StyleSheet.create({
  flex: { flex: 1, backgroundColor: colors.neutral050 },
  scroll: { padding: spacing.md, gap: spacing.sm, paddingBottom: 48 },
  loading: { paddingVertical: spacing.xl, alignItems: 'center' },
  segment: {
    flexDirection: 'row',
    margin: spacing.md,
    marginBottom: 0,
    backgroundColor: colors.white,
    borderWidth: 1,
    borderColor: colors.neutral200,
    borderRadius: radius.md,
    padding: 4,
    gap: 4,
  },
  segBtn: {
    flex: 1,
    minHeight: 40,
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: radius.sm,
  },
  segBtnOn: { backgroundColor: colors.primary050 },
  segText: { fontSize: type.body.fontSize, fontWeight: '600', color: colors.neutral500 },
  segTextOn: { color: colors.primary700 },
  // 혼잡
  overallCard: {
    backgroundColor: colors.white,
    borderWidth: 1,
    borderColor: colors.neutral200,
    borderRadius: radius.md,
    padding: spacing.md,
    gap: 10,
  },
  overallTop: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
  overallLabel: { fontSize: type.h3.fontSize, fontWeight: '700', color: colors.neutral900 },
  overallMetric: { flexDirection: 'row', alignItems: 'center', gap: 6 },
  overallCount: { fontSize: type.body.fontSize, color: colors.neutral700, fontWeight: '600' },
  group: {
    backgroundColor: colors.white,
    borderWidth: 1,
    borderColor: colors.neutral200,
    borderRadius: radius.md,
    padding: spacing.md,
    gap: 8,
  },
  groupHeader: { flexDirection: 'row', alignItems: 'center', gap: 6 },
  groupTitle: { fontSize: type.body.fontSize, fontWeight: '700', color: colors.neutral900 },
  areaRow: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    gap: 10,
    borderTopWidth: 1,
    borderTopColor: colors.neutral200,
    paddingTop: 8,
  },
  areaLabel: { flex: 1, fontSize: type.body.fontSize, color: colors.neutral700 },
  // 주차 lot
  lotCard: {
    backgroundColor: colors.white,
    borderWidth: 1,
    borderColor: colors.neutral200,
    borderRadius: radius.md,
    padding: spacing.md,
    gap: 8,
  },
  lotTop: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 8 },
  lotName: { flex: 1, fontSize: type.h3.fontSize, fontWeight: '700', color: colors.neutral900 },
  bar: {
    height: 8,
    borderRadius: 4,
    backgroundColor: colors.neutral200,
    overflow: 'hidden',
  },
  barFill: { height: 8, borderRadius: 4, backgroundColor: colors.primary600 },
  lotMetaRow: { flexDirection: 'row' },
  lotMeta: { fontSize: type.caption.fontSize, color: colors.neutral500 },
  buyBtn: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 6,
    minHeight: touch.min,
    borderRadius: radius.sm,
    backgroundColor: colors.primary600,
    marginTop: 2,
  },
  buyBtnGhost: { backgroundColor: colors.neutral050, borderWidth: 1, borderColor: colors.neutral200 },
  buyText: { color: colors.white, fontSize: type.body.fontSize, fontWeight: '700' },
  buyHint: { fontSize: 11, color: colors.neutral500, textAlign: 'center' },
  // 주차권
  loginNote: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    backgroundColor: colors.primary050,
    borderRadius: radius.md,
    paddingHorizontal: 14,
    paddingVertical: 12,
  },
  loginNoteText: { fontSize: type.body.fontSize, color: colors.primary700, fontWeight: '600' },
  emptyNote: { fontSize: type.caption.fontSize, color: colors.neutral500, paddingVertical: spacing.sm },
  passCard: {
    backgroundColor: colors.white,
    borderWidth: 1,
    borderColor: colors.neutral200,
    borderRadius: radius.md,
    padding: spacing.md,
    gap: 6,
  },
  passDim: { opacity: 0.6 },
  passTop: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 8 },
  passLot: { flex: 1, fontSize: type.body.fontSize, fontWeight: '700', color: colors.neutral900 },
  passBadge: { borderRadius: radius.pill, paddingHorizontal: 10, paddingVertical: 3 },
  passBadgeText: { fontSize: 11, fontWeight: '700' },
  passMeta: { fontSize: type.caption.fontSize, color: colors.neutral500 },
  passBottom: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
  passAmount: { fontSize: type.body.fontSize, fontWeight: '700', color: colors.primary700 },
  // 모달
  modalBackdrop: { flex: 1, backgroundColor: 'rgba(16,24,40,0.45)', justifyContent: 'flex-end' },
  modalSheet: {
    backgroundColor: colors.white,
    borderTopLeftRadius: 16,
    borderTopRightRadius: 16,
    padding: spacing.md,
    paddingBottom: spacing.xl,
    gap: 8,
  },
  modalHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
  modalTitle: { fontSize: type.h2.fontSize, fontWeight: '700', color: colors.neutral900 },
  modalLabel: { fontSize: type.caption.fontSize, color: colors.neutral500, fontWeight: '600', marginTop: 6 },
  modalLotBox: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    backgroundColor: colors.neutral050,
    borderRadius: radius.sm,
    padding: 12,
  },
  modalLotName: { flex: 1, fontSize: type.body.fontSize, fontWeight: '600', color: colors.neutral900 },
  modalLotPrice: { fontSize: type.body.fontSize, fontWeight: '700', color: colors.primary700 },
  dateRow: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    backgroundColor: colors.neutral050,
    borderRadius: radius.sm,
    borderWidth: 1,
    borderColor: colors.neutral200,
  },
  dateBtn: { width: touch.min, height: touch.min, alignItems: 'center', justifyContent: 'center' },
  dateText: { fontSize: type.h3.fontSize, fontWeight: '700', color: colors.neutral900 },
  input: {
    minHeight: touch.min,
    borderWidth: 1,
    borderColor: colors.neutral200,
    borderRadius: radius.sm,
    paddingHorizontal: 12,
    fontSize: type.body.fontSize,
    color: colors.neutral900,
    backgroundColor: colors.white,
  },
  modalNote: { fontSize: 11, color: colors.neutral500, lineHeight: 16 },
  confirmBtn: {
    minHeight: 52,
    borderRadius: radius.md,
    backgroundColor: colors.primary600,
    alignItems: 'center',
    justifyContent: 'center',
    marginTop: 8,
  },
  confirmBusy: { opacity: 0.7 },
  confirmText: { color: colors.white, fontSize: type.h3.fontSize, fontWeight: '700' },
});
