import { useRef } from "react";
import { StyleSheet, View, Text, Pressable, type LayoutChangeEvent } from "react-native";
import { CameraView, useCameraPermissions, type BarcodeScanningResult } from "expo-camera";
import { theme } from "../lib/theme";

export interface QRScannerProps {
  onScan: (code: string) => void;
  active: boolean;
}

// Side of the accept square, matching the visible reticle (index.tsx uses 240),
// with a little tolerance so a code aimed inside the box always registers.
const RETICLE = 260;

/** Native (Android/iOS) scanner using expo-camera. Only accepts codes whose
 * position falls within the centered reticle square. */
export default function QRScanner({ onScan, active }: QRScannerProps) {
  const [permission, requestPermission] = useCameraPermissions();
  const lastScan = useRef<{ code: string; at: number }>({ code: "", at: 0 });
  const layout = useRef({ w: 0, h: 0 });

  const onLayout = (e: LayoutChangeEvent) => {
    layout.current = { w: e.nativeEvent.layout.width, h: e.nativeEvent.layout.height };
  };

  // True if the scanned code sits inside the centered reticle. Fails OPEN when
  // geometry is missing/unknown so scanning never silently breaks.
  const inReticle = (res: BarcodeScanningResult): boolean => {
    const { w, h } = layout.current;
    if (!w || !h) return true;
    const pts = res.cornerPoints as { x: number; y: number }[] | undefined;
    let cx: number | undefined;
    let cy: number | undefined;
    if (pts && pts.length) {
      cx = pts.reduce((s, p) => s + p.x, 0) / pts.length;
      cy = pts.reduce((s, p) => s + p.y, 0) / pts.length;
    } else {
      const b: any = (res as any).bounds;
      if (b?.origin && b?.size) {
        cx = b.origin.x + b.size.width / 2;
        cy = b.origin.y + b.size.height / 2;
      }
    }
    if (cx === undefined || cy === undefined) return true;
    // Some platforms report normalized [0,1] coords — scale to view size.
    if (cx <= 1 && cy <= 1) {
      cx *= w;
      cy *= h;
    }
    const half = RETICLE / 2;
    return Math.abs(cx - w / 2) <= half && Math.abs(cy - h / 2) <= half;
  };

  if (!permission) {
    return <View style={styles.fill} />;
  }
  if (!permission.granted) {
    return (
      <View style={[styles.fill, styles.center]}>
        <Text style={styles.msg}>Camera access is needed to scan tickets.</Text>
        <Pressable style={styles.btn} onPress={requestPermission}>
          <Text style={styles.btnText}>Grant camera permission</Text>
        </Pressable>
      </View>
    );
  }

  return (
    <CameraView
      style={styles.fill}
      facing="back"
      onLayout={onLayout}
      barcodeScannerSettings={{ barcodeTypes: ["qr"] }}
      onBarcodeScanned={
        active
          ? (res) => {
              if (!inReticle(res)) return; // ignore codes outside the square
              const data = res.data;
              const now = Date.now();
              // Debounce repeated frames of the same code.
              if (data === lastScan.current.code && now - lastScan.current.at < 3000) return;
              lastScan.current = { code: data, at: now };
              onScan(data);
            }
          : undefined
      }
    />
  );
}

const styles = StyleSheet.create({
  fill: { flex: 1, width: "100%", height: "100%" },
  center: { alignItems: "center", justifyContent: "center", padding: 24, backgroundColor: theme.bg },
  msg: { color: theme.text, fontSize: 16, textAlign: "center", marginBottom: 20 },
  btn: { backgroundColor: theme.primary, paddingHorizontal: 20, paddingVertical: 12, borderRadius: 10 },
  btnText: { color: "#fff", fontSize: 16, fontWeight: "600" },
});
