/**
 * Live camera text capture — generic reference (Expo / React Native).
 *
 * In a typical project this logic may be split across e.g.:
 *   app/scan-camera.tsx, utils/documentExtraction.ts, prepareImageForOcr.ts, mapTextBlocksToPreview.ts
 *
 * This file inlines everything for docs or reuse. It is not wired as a route here — adapt paths
 * and navigation to your app. Copy as needed for a single-file printout or teaching demo.
 * Post-capture navigation is omitted here: final OCR shows the extracted text in an alert only.
 * No ScreenHeader / ThemeContext: inline header + `REF` colors only.
 * User-visible strings are plain English in `REF_MSG` (no i18n).
 */

import { CameraView, useCameraPermissions } from 'expo-camera';
import * as ImageManipulator from 'expo-image-manipulator';
import {
  extractTextBlocksFromImage,
  extractTextFromImage,
  isSupported,
  type TextBlockBounds,
} from 'expo-text-extractor';
import * as FileSystem from 'expo-file-system/legacy';
import { useRouter } from 'expo-router';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import {
  ActivityIndicator,
  Alert,
  Platform,
  Pressable,
  StyleSheet,
  Text,
  View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

/** Inline palette so this file has no ScreenHeader / ThemeContext deps. */
const REF = {
  accent: '#818CF8',
  surface: '#111827',
  border: '#25314D',
  webBg: '#F3F4F9',
  textSecondary: '#9CA3AF',
} as const;

/** Copy for this reference file only; production screens use i18n. */
const REF_MSG = {
  screenTitle: 'Live text scan',
  goBack: 'Go back',
  ocrWebTitle: 'Camera OCR is not available here',
  ocrWebMessage:
    'Open this screen in your native iOS or Android build. On-device text recognition does not run in web browsers.',
  ocrFailedTitle: 'Could not read text from the image',
  errWebOcr: 'The browser cannot run on-device text recognition. Use the mobile app.',
  errDeviceUnsupported: 'Text recognition is not available on this device or build.',
  errEmptyOcr:
    'No text was detected. Use brighter light, hold the phone steady, and move closer so the text is sharp.',
  errUnknown: 'Something went wrong. Please try again.',
  cameraDenied: 'Camera access is required to scan documents.',
  grantCamera: 'Allow camera',
  liveHint: 'Point the camera at printed text. Overlays mark regions the device can read.',
  startingCamera: 'Starting camera…',
  captureLabel: 'Capture and extract text',
  ocrResultTitle: 'Extracted text',
  charCountLabel: (n: number) => `Characters: ${n}`,
} as const;

// —— documentExtraction (inlined) ——————————————————————————————————

export const OCR_ERROR_WEB = 'OCR_WEB';
export const OCR_ERROR_UNSUPPORTED = 'OCR_UNSUPPORTED';
export const OCR_ERROR_EMPTY = 'OCR_EMPTY';

export async function extractPrintedTextFromImageUri(imageUri: string): Promise<string> {
  if (Platform.OS === 'web') {
    throw new Error(OCR_ERROR_WEB);
  }
  if (!isSupported) {
    throw new Error(OCR_ERROR_UNSUPPORTED);
  }

  const segments = await extractTextFromImage(imageUri);
  const text = segments
    .map((s) => s.trim())
    .filter(Boolean)
    .join('\n')
    .trim();

  if (!text) {
    throw new Error(OCR_ERROR_EMPTY);
  }

  return text;
}

export async function extractPrintedTextBlocksFromImageUri(
  imageUri: string
): Promise<TextBlockBounds[]> {
  if (Platform.OS === 'web') {
    throw new Error(OCR_ERROR_WEB);
  }
  if (!isSupported) {
    throw new Error(OCR_ERROR_UNSUPPORTED);
  }
  return extractTextBlocksFromImage(imageUri);
}

// —— prepareImageForOcr (inlined) ————————————————————————————————

const OCR_MAX_EDGE_PX = 1600;

async function prepareImageForOcr(
  uri: string,
  width?: number | null,
  height?: number | null
): Promise<string> {
  const maxEdge = OCR_MAX_EDGE_PX;
  const w = width ?? 0;
  const h = height ?? 0;
  const actions: ImageManipulator.Action[] =
    w > 0 && h > 0 && (w > maxEdge || h > maxEdge)
      ? w >= h
        ? [{ resize: { width: maxEdge } }]
        : [{ resize: { height: maxEdge } }]
      : [];
  const result = await ImageManipulator.manipulateAsync(uri, actions, {
    compress: 0.82,
    format: ImageManipulator.SaveFormat.JPEG,
  });
  return result.uri;
}

// —— mapTextBlocksToPreview (inlined) —————————————————————————————

export type PreviewHighlightRect = {
  left: number;
  top: number;
  width: number;
  height: number;
};

function mapTextBlocksToPreview(
  blocks: TextBlockBounds[],
  imageWidth: number,
  imageHeight: number,
  viewWidth: number,
  viewHeight: number,
  maxBoxes = 28
): PreviewHighlightRect[] {
  if (!imageWidth || !imageHeight || !viewWidth || !viewHeight || blocks.length === 0) {
    return [];
  }

  const scale = Math.max(viewWidth / imageWidth, viewHeight / imageHeight);
  const dispW = imageWidth * scale;
  const dispH = imageHeight * scale;
  const offX = (viewWidth - dispW) / 2;
  const offY = (viewHeight - dispH) / 2;

  return blocks.slice(0, maxBoxes).map((b) => ({
    left: offX + b.x * dispW,
    top: offY + b.y * dispH,
    width: Math.max(1, b.width * dispW),
    height: Math.max(1, b.height * dispH),
  }));
}

// —— scan-camera UI (inlined) ———————————————————————————————————

function accentToRgba(hex: string, alpha: number): string {
  const raw = hex.replace('#', '');
  if (raw.length !== 6 || Number.isNaN(parseInt(raw, 16))) {
    return `rgba(129, 140, 248, ${alpha})`;
  }
  const n = parseInt(raw, 16);
  const r = (n >> 16) & 255;
  const g = (n >> 8) & 255;
  const b = n & 255;
  return `rgba(${r},${g},${b},${alpha})`;
}

function ScanTextRegionHighlight({
  rect,
  accent,
}: {
  rect: PreviewHighlightRect;
  accent: string;
}) {
  const { left, top, width, height } = rect;
  const minSide = Math.min(width, height);
  const arm = Math.min(22, Math.max(9, Math.round(minSide * 0.42)));
  const stroke = 2;
  const cornerRadius = Math.min(5, Math.max(2, minSide * 0.12));
  const fill = accentToRgba(accent, 0.09);
  const glow = accentToRgba(accent, 0.22);

  const base = { position: 'absolute' as const, borderColor: accent };

  return (
    <>
      <View
        style={[
          base,
          {
            left,
            top,
            width,
            height,
            borderRadius: cornerRadius,
            backgroundColor: fill,
          },
        ]}
      />
      <View
        style={[
          base,
          {
            left: left - 0.5,
            top: top - 0.5,
            width: arm + 0.5,
            height: arm + 0.5,
            borderTopWidth: stroke,
            borderLeftWidth: stroke,
            borderTopLeftRadius: cornerRadius,
          },
        ]}
      />
      <View
        style={[
          base,
          {
            left: left + width - arm,
            top: top - 0.5,
            width: arm + 0.5,
            height: arm + 0.5,
            borderTopWidth: stroke,
            borderRightWidth: stroke,
            borderTopRightRadius: cornerRadius,
          },
        ]}
      />
      <View
        style={[
          base,
          {
            left: left - 0.5,
            top: top + height - arm,
            width: arm + 0.5,
            height: arm + 0.5,
            borderBottomWidth: stroke,
            borderLeftWidth: stroke,
            borderBottomLeftRadius: cornerRadius,
          },
        ]}
      />
      <View
        style={[
          base,
          {
            left: left + width - arm,
            top: top + height - arm,
            width: arm + 0.5,
            height: arm + 0.5,
            borderBottomWidth: stroke,
            borderRightWidth: stroke,
            borderBottomRightRadius: cornerRadius,
          },
        ]}
      />
      <View
        pointerEvents="none"
        style={{
          position: 'absolute',
          left: left + stroke,
          top: top + stroke,
          width: Math.max(0, width - stroke * 2),
          height: Math.max(0, height - stroke * 2),
          borderRadius: Math.max(0, cornerRadius - 1),
          borderWidth: 1,
          borderColor: glow,
        }}
      />
    </>
  );
}

const LIVE_INTERVAL_MS = 820;
const LIVE_CAPTURE_QUALITY = 0.22;
const FINAL_CAPTURE_QUALITY = 0.78;
const OCR_RESULT_PREVIEW_MAX = 3500;

export default function LiveTextCaptureReferenceScreen() {
  const router = useRouter();
  const [permission, requestPermission] = useCameraPermissions();
  const cameraRef = useRef<CameraView>(null);
  const [cameraReady, setCameraReady] = useState(false);
  const [previewLayout, setPreviewLayout] = useState<{ width: number; height: number } | null>(null);
  const [highlights, setHighlights] = useState<PreviewHighlightRect[]>([]);
  const [busy, setBusy] = useState(false);
  const liveBusyRef = useRef(false);

  const runFinalOcr = useCallback(
    async (imageUri: string, width?: number | null, height?: number | null) => {
      if (Platform.OS === 'web') {
        Alert.alert(REF_MSG.ocrWebTitle, REF_MSG.ocrWebMessage);
        return;
      }
      setBusy(true);
      try {
        const preparedUri = await prepareImageForOcr(imageUri, width, height);
        const text = await extractPrintedTextFromImageUri(preparedUri);
        const body =
          text.length > OCR_RESULT_PREVIEW_MAX
            ? `${text.slice(0, OCR_RESULT_PREVIEW_MAX)}…`
            : text;
        Alert.alert(
          REF_MSG.ocrResultTitle,
          `${REF_MSG.charCountLabel(text.length)}\n\n${body}`
        );
      } catch (e) {
        const code = e instanceof Error ? e.message : '';
        let msg: string;
        if (code === OCR_ERROR_WEB) {
          msg = REF_MSG.errWebOcr;
        } else if (code === OCR_ERROR_UNSUPPORTED) {
          msg = REF_MSG.errDeviceUnsupported;
        } else if (code === OCR_ERROR_EMPTY) {
          msg = REF_MSG.errEmptyOcr;
        } else {
          msg = e instanceof Error ? e.message : REF_MSG.errUnknown;
        }
        Alert.alert(REF_MSG.ocrFailedTitle, msg);
      } finally {
        setBusy(false);
      }
    },
    []
  );

  useEffect(() => {
    if (Platform.OS === 'web') {
      Alert.alert(REF_MSG.ocrWebTitle, REF_MSG.ocrWebMessage, [
        { text: 'OK', onPress: () => router.back() },
      ]);
    }
  }, [router]);

  useEffect(() => {
    if (Platform.OS === 'web') return;
    void requestPermission();
  }, [requestPermission]);

  useEffect(() => {
    if (
      Platform.OS === 'web' ||
      !cameraReady ||
      !permission?.granted ||
      !previewLayout ||
      busy
    ) {
      return;
    }

    const id = setInterval(async () => {
      const cam = cameraRef.current;
      if (!cam || liveBusyRef.current) return;
      liveBusyRef.current = true;
      let uri: string | null = null;
      try {
        const pic = await cam.takePictureAsync({
          quality: LIVE_CAPTURE_QUALITY,
          exif: false,
          shutterSound: false,
        });
        uri = pic.uri;
        const blocks = await extractPrintedTextBlocksFromImageUri(pic.uri);
        setHighlights(
          mapTextBlocksToPreview(
            blocks,
            pic.width,
            pic.height,
            previewLayout.width,
            previewLayout.height
          )
        );
      } catch {
        setHighlights([]);
      } finally {
        if (uri) {
          void FileSystem.deleteAsync(uri, { idempotent: true });
        }
        liveBusyRef.current = false;
      }
    }, LIVE_INTERVAL_MS);

    return () => clearInterval(id);
  }, [busy, cameraReady, permission?.granted, previewLayout]);

  const onCapture = async () => {
    if (busy || !cameraRef.current || Platform.OS === 'web') return;
    try {
      const pic = await cameraRef.current.takePictureAsync({
        quality: FINAL_CAPTURE_QUALITY,
        shutterSound: false,
      });
      await runFinalOcr(pic.uri, pic.width, pic.height);
    } catch {
      Alert.alert(REF_MSG.ocrFailedTitle, REF_MSG.errUnknown);
    }
  };

  if (Platform.OS === 'web') {
    return (
      <SafeAreaView style={[styles.fill, { backgroundColor: REF.webBg }]}>
        <View style={[styles.headerBar, styles.headerBarWeb]}>
          <View style={styles.headerSide}>
            <Pressable
              onPress={() => router.back()}
              accessibilityRole="button"
              accessibilityLabel={REF_MSG.goBack}
              style={({ pressed }) => [styles.headerBackPressable, pressed && { opacity: 0.7 }]}
            >
              <Text style={styles.headerBackGlyphWeb}>←</Text>
            </Pressable>
          </View>
          <Text style={styles.headerTitleWeb} numberOfLines={1}>
            {REF_MSG.screenTitle}
          </Text>
          <View style={styles.headerSide} />
        </View>
      </SafeAreaView>
    );
  }

  return (
    <SafeAreaView style={[styles.fill, { backgroundColor: '#000' }]} edges={['top']}>
      <View style={styles.headerBar}>
        <View style={styles.headerSide}>
          <Pressable
            onPress={() => router.back()}
            accessibilityRole="button"
            accessibilityLabel={REF_MSG.goBack}
            style={({ pressed }) => [styles.headerBackPressable, pressed && { opacity: 0.7 }]}
          >
            <Text style={styles.headerBackGlyph}>←</Text>
          </Pressable>
        </View>
        <Text style={styles.headerTitle} numberOfLines={1}>
          {REF_MSG.screenTitle}
        </Text>
        <View style={styles.headerSide} />
      </View>
      <View
        style={styles.previewWrap}
        onLayout={(e) => {
          const { width, height } = e.nativeEvent.layout;
          if (width > 0 && height > 0) {
            setPreviewLayout({ width, height });
          }
        }}
      >
        {!permission?.granted ? (
          <View style={styles.centerMessage}>
            <Text style={styles.hintLight}>{REF_MSG.cameraDenied}</Text>
            <Pressable
              onPress={() => void requestPermission()}
              style={({ pressed }) => [styles.primaryBtn, pressed && styles.primaryBtnPressed]}
            >
              <Text style={styles.primaryBtnText}>{REF_MSG.grantCamera}</Text>
            </Pressable>
          </View>
        ) : (
          <>
            <CameraView
              ref={cameraRef}
              style={StyleSheet.absoluteFill}
              facing="back"
              mode="picture"
              animateShutter={false}
              onCameraReady={() => setCameraReady(true)}
            />
            <View style={styles.overlay} pointerEvents="none">
              {highlights.map((r, i) => (
                <ScanTextRegionHighlight
                  key={`${r.left.toFixed(1)}-${r.top.toFixed(1)}-${i}`}
                  rect={r}
                  accent={REF.accent}
                />
              ))}
            </View>
            <View style={styles.hintBanner} pointerEvents="none">
              <Text style={styles.hintBannerText}>{REF_MSG.liveHint}</Text>
            </View>
          </>
        )}
        {!cameraReady && permission?.granted ? (
          <View style={styles.startingOverlay}>
            <ActivityIndicator color={REF.accent} />
            <Text style={styles.startingText}>{REF_MSG.startingCamera}</Text>
          </View>
        ) : null}
      </View>

      <View style={[styles.footer, { borderTopColor: REF.border, backgroundColor: REF.surface }]}>
        <Pressable
          onPress={() => void onCapture()}
          disabled={busy || !permission?.granted || !cameraReady}
          style={({ pressed }) => [
            styles.captureOuter,
            (busy || !permission?.granted || !cameraReady) && styles.captureOuterDisabled,
            pressed && !busy && permission?.granted && cameraReady && styles.captureOuterPressed,
          ]}
        >
          {busy ? (
            <ActivityIndicator color={REF.accent} />
          ) : (
            <View style={[styles.captureInner, { borderColor: REF.accent }]} />
          )}
        </Pressable>
        <Text style={[styles.footerLabel, { color: REF.textSecondary }]}>
          {REF_MSG.captureLabel}
        </Text>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  headerBar: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 10,
    paddingHorizontal: 8,
    backgroundColor: '#000',
  },
  headerBarWeb: {
    backgroundColor: REF.webBg,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: '#DCE2F1',
  },
  headerSide: {
    width: 44,
    alignItems: 'center',
    justifyContent: 'center',
  },
  headerBackPressable: {
    paddingVertical: 8,
    paddingHorizontal: 10,
  },
  headerBackGlyph: {
    color: '#fff',
    fontSize: 24,
    fontWeight: '600',
  },
  headerBackGlyphWeb: {
    color: '#111827',
    fontSize: 24,
    fontWeight: '600',
  },
  headerTitle: {
    flex: 1,
    textAlign: 'center',
    color: '#fff',
    fontSize: 17,
    fontWeight: '700',
  },
  headerTitleWeb: {
    flex: 1,
    textAlign: 'center',
    color: '#111827',
    fontSize: 17,
    fontWeight: '700',
  },
  fill: {
    flex: 1,
  },
  previewWrap: {
    flex: 1,
    position: 'relative',
    overflow: 'hidden',
  },
  overlay: {
    ...StyleSheet.absoluteFillObject,
  },
  hintBanner: {
    position: 'absolute',
    left: 12,
    right: 12,
    top: 12,
    paddingVertical: 8,
    paddingHorizontal: 12,
    borderRadius: 10,
    backgroundColor: 'rgba(0,0,0,0.55)',
  },
  hintBannerText: {
    color: 'rgba(255,255,255,0.92)',
    fontSize: 14,
    textAlign: 'center',
  },
  centerMessage: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    padding: 24,
    gap: 16,
  },
  hintLight: {
    color: 'rgba(255,255,255,0.85)',
    fontSize: 16,
    textAlign: 'center',
  },
  primaryBtn: {
    paddingVertical: 14,
    paddingHorizontal: 22,
    borderRadius: 14,
    backgroundColor: '#facc15',
  },
  primaryBtnPressed: {
    opacity: 0.88,
  },
  primaryBtnText: {
    color: '#111',
    fontSize: 16,
    fontWeight: '600',
  },
  startingOverlay: {
    ...StyleSheet.absoluteFillObject,
    backgroundColor: 'rgba(0,0,0,0.35)',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 12,
  },
  startingText: {
    color: 'rgba(255,255,255,0.9)',
    fontSize: 15,
  },
  footer: {
    paddingTop: 18,
    paddingBottom: 24,
    paddingHorizontal: 24,
    alignItems: 'center',
    borderTopWidth: StyleSheet.hairlineWidth,
  },
  footerLabel: {
    marginTop: 10,
    fontSize: 14,
  },
  captureOuter: {
    width: 76,
    height: 76,
    borderRadius: 38,
    borderWidth: 4,
    borderColor: 'rgba(255,255,255,0.85)',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(255,255,255,0.12)',
  },
  captureOuterDisabled: {
    opacity: 0.45,
  },
  captureOuterPressed: {
    opacity: 0.85,
    transform: [{ scale: 0.97 }],
  },
  captureInner: {
    width: 58,
    height: 58,
    borderRadius: 29,
    borderWidth: 3,
    backgroundColor: 'rgba(255,255,255,0.25)',
  },
});
