import { useRef, useState } from "react";
import { Alert, ScrollView, StyleSheet, View } from "react-native";
import { router } from "expo-router";
import * as ImagePicker from "expo-image-picker";
import { CameraView, useCameraPermissions } from "expo-camera";
import { ALLERGEN_LABELS_SV, type Allergen } from "@app/shared-types";
import { api, uploadImage, ApiError } from "@/lib/api";
import { t } from "@/lib/i18n";
import { Body, Button, Card, Heading, Row, Screen, Small, Spacer } from "@/components/ui";
import { colors, spacing } from "@/lib/theme";

/**
 * Streckkodsskanning (spec §11): läses LOKALT med kameran, slås upp mot
 * egen produktdatabas → Open Food Facts. Rent INFORMATIONSFLÖDE – visar
 * vad produkten innehåller (näring, allergener, ingredienser). Ingen
 * "lägg till"-funktion; man skannar för att se innehållet.
 *
 * Buggfix: kameran triggar onBarcodeScanned många gånger/sekund. Tidigare
 * användes state-flaggor som uppdateras asynkront → flera parallella
 * uppslag och staplade Alert-rutor ("okänd" först, produkten bakom –
 * syntes först efter man tryckt Avbryt). Nu låser vi synkront med en ref
 * och visar resultatet i en egen panel i stället för Alert.
 */

type Nutrition = {
  values: {
    kcal: number;
    proteinG: number;
    carbsG: number;
    fatG: number;
    saturatedFatG: number;
    monounsaturatedFatG?: number;
    polyunsaturatedFatG?: number;
    fiberG: number;
    sugarG: number;
    saltG: number;
  };
} | null;

interface ProductInfo {
  name: string;
  brand: string | null;
  packageSizeValue: number | null;
  packageSizeUnit: string | null;
  ingredientsText: string | null;
  allergens: string[] | null;
  mayContainAllergens: string[] | null;
  nutrition: Nutrition;
  dataSource: string | null;
}
interface BarcodeScanResponse {
  product: ProductInfo | null;
}
interface CreateScanResponse {
  scan: { id: string };
  uploads: Array<{ key: string; uploadUrl: string; headers: Record<string, string> }>;
}
interface ScanJobResponse {
  status: string;
  result: { product: ProductInfo | null } | null;
  error?: string | null;
}

/**
 * Pollar en produktskanning tills AAMOS läst av förpackningen. Vid "completed"
 * returneras produkten (redan sparad i delade katalogen av workern), vid
 * "failed"/timeout returneras null. Max ~60 s (40 × 1,5 s).
 */
async function pollScan(id: string): Promise<ProductInfo | null> {
  for (let i = 0; i < 40; i++) {
    await new Promise((r) => setTimeout(r, 1500));
    const job = await api<ScanJobResponse>(`/v1/scans/${id}`);
    if (job.status === "completed") return job.result?.product ?? null;
    if (job.status === "failed") return null;
  }
  return null;
}

const fmtG = (n: number) => `${(Math.round(n * 10) / 10).toString().replace(".", ",")} g`;
const allergenLabels = (codes: string[] | null | undefined): string =>
  (codes ?? [])
    .map((c) => ALLERGEN_LABELS_SV[c as Allergen] ?? c)
    .filter(Boolean)
    .join(", ");

export default function BarcodeScreen() {
  const [permission, requestPermission] = useCameraPermissions();
  const [busy, setBusy] = useState(false);
  const [product, setProduct] = useState<ProductInfo | null>(null);
  const [unknown, setUnknown] = useState(false);
  const [contributing, setContributing] = useState(false);
  const [justContributed, setJustContributed] = useState(false);
  const [errorMsg, setErrorMsg] = useState<string | null>(null);
  // Synkron lås: hindrar att kamerans upprepade träffar startar parallella uppslag.
  const locked = useRef(false);
  // Senast avlästa streckkod – används när användaren fotar en okänd produkt.
  const lastGtin = useRef<string | null>(null);

  const scanning = !busy && !product && !unknown && !errorMsg;

  const onScanned = async (gtin: string) => {
    if (locked.current) return;
    locked.current = true;
    lastGtin.current = gtin;
    setBusy(true);
    setErrorMsg(null);
    setJustContributed(false);
    try {
      const result = await api<BarcodeScanResponse>("/v1/scans", {
        method: "POST",
        body: { scanType: "barcode", barcode: gtin, imageCount: 0 },
      });
      if (result.product) setProduct(result.product);
      else setUnknown(true);
    } catch (err) {
      setErrorMsg(err instanceof Error ? err.message : t("common.error"));
    } finally {
      setBusy(false);
    }
  };

  /**
   * Okänd produkt → fota förpackningen. Bilden läses av (näring/allergener/
   * innehåll) och produkten sparas i den DELADE katalogen kopplat till
   * streckkoden – så nästa person som skannar samma kod får den direkt.
   * Lägger INTE till något bland användarens egna varor (spec §11).
   */
  // Fota fram- OCH baksida: framsidan ger namn/varumärke, baksidan näring +
  // innehåll. AI:n slår ihop bilderna. Kamera med galleri-fallback.
  const capturePackagePhotos = async (): Promise<string[]> => {
    const perm = await ImagePicker.requestCameraPermissionsAsync();
    const shoot = () =>
      perm.granted
        ? ImagePicker.launchCameraAsync({ quality: 0.7 })
        : ImagePicker.launchImageLibraryAsync({ quality: 0.7 });
    const uris: string[] = [];
    const first = await shoot();
    const a0 = first.assets?.[0];
    if (first.canceled || !a0) return uris;
    uris.push(a0.uri);
    const wantBack = await new Promise<boolean>((resolve) =>
      Alert.alert(t("barcode.contributing.frontDone"), t("barcode.contributing.backPrompt"), [
        {
          text: t("barcode.contributing.skipBack"),
          style: "cancel",
          onPress: () => resolve(false),
        },
        { text: t("barcode.contributing.shootBack"), onPress: () => resolve(true) },
      ]),
    );
    if (wantBack) {
      const second = await shoot();
      const a1 = second.assets?.[0];
      if (!second.canceled && a1) uris.push(a1.uri);
    }
    return uris;
  };

  const contributePhoto = async () => {
    const gtin = lastGtin.current;
    if (!gtin) return;
    try {
      const uris = await capturePackagePhotos();
      if (uris.length === 0) return; // avbröt – stanna kvar i nuvarande vy

      setContributing(true);

      // Produktskanning kopplad till streckkoden – delad katalog, inte lagret.
      const created = await api<CreateScanResponse>("/v1/scans", {
        method: "POST",
        body: {
          scanType: "product_package",
          imageCount: uris.length,
          contentType: "image/jpeg",
          context: { barcode: gtin },
        },
      });
      for (let i = 0; i < uris.length; i++) {
        const upload = created.uploads[i];
        if (!upload) throw new Error(t("common.error"));
        await uploadImage(upload, uris[i]!);
      }
      await api(`/v1/scans/${created.scan.id}/start`, { method: "POST" });

      const found = await pollScan(created.scan.id);
      if (found) {
        setUnknown(false);
        setJustContributed(true);
        setProduct(found);
      } else {
        // Behåll nuvarande vy (okänd/produkt) och visa felet som dialog.
        Alert.alert(t("common.oops"), t("barcode.contributing.failed"));
      }
    } catch (err) {
      if (err instanceof ApiError && err.code === "QUOTA_EXCEEDED") {
        router.push("/paywall");
      } else {
        Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error"));
      }
    } finally {
      setContributing(false);
    }
  };

  const scanAgain = () => {
    setProduct(null);
    setUnknown(false);
    setErrorMsg(null);
    setJustContributed(false);
    locked.current = false;
    lastGtin.current = null;
  };

  if (!permission)
    return (
      <Screen>
        <Body>{t("scan.analyzing")}</Body>
      </Screen>
    );
  if (!permission.granted) {
    return (
      <Screen>
        <Card>
          <Heading>{t("barcode.cameraTitle")}</Heading>
          <Body>{t("barcode.cameraBody")}</Body>
          <Spacer size={8} />
          <Button label={t("barcode.allowCamera")} onPress={() => void requestPermission()} />
        </Card>
      </Screen>
    );
  }

  const n = product?.nutrition?.values;
  const allergens = allergenLabels(product?.allergens);
  const mayContain = allergenLabels(product?.mayContainAllergens);
  // Saknar produkten näring eller innehåll? Erbjud att komplettera med baksidesfoto.
  const incomplete = !!product && (!n || !product.ingredientsText);

  return (
    <View style={styles.container}>
      <CameraView
        style={StyleSheet.absoluteFill}
        active={scanning}
        barcodeScannerSettings={{ barcodeTypes: ["ean13", "ean8", "upc_a", "upc_e"] }}
        onBarcodeScanned={(scan) => void onScanned(scan.data)}
      />

      {/* Siktruta medan man skannar */}
      {scanning && (
        <View style={styles.overlay}>
          <View style={styles.frame} />
          <Small style={styles.hint}>{t("barcode.aim")}</Small>
        </View>
      )}

      {busy && (
        <View style={styles.overlay}>
          <Small style={styles.hint}>{t("scan.analyzing")}</Small>
        </View>
      )}

      {/* Produktinfo-panel (nedre delen, scrollbar) */}
      {product && !contributing && (
        <View style={styles.panel}>
          <ScrollView contentContainerStyle={{ padding: spacing.lg, paddingBottom: spacing.md }}>
            {justContributed && (
              <View style={styles.savedBanner}>
                <Small style={styles.savedText}>{t("barcode.contributing.saved")}</Small>
              </View>
            )}
            <Heading>{product.name}</Heading>
            {(product.brand || product.packageSizeValue) && (
              <Small style={styles.muted}>
                {[
                  product.brand ?? undefined,
                  product.packageSizeValue
                    ? `${product.packageSizeValue}${product.packageSizeUnit ? " " + product.packageSizeUnit : ""}`
                    : undefined,
                ]
                  .filter(Boolean)
                  .join(" · ")}
              </Small>
            )}

            <Spacer size={spacing.sm} />
            {n ? (
              <View style={styles.nutBox}>
                <Small style={styles.nutTitle}>{t("barcode.info.title")}</Small>
                <NutRow
                  label={t("barcode.info.kcal")}
                  value={`${Math.round(n.kcal)} kcal`}
                  strong
                />
                <NutRow label={t("barcode.info.fat")} value={fmtG(n.fatG)} />
                <NutRow label={t("barcode.info.satfat")} value={fmtG(n.saturatedFatG)} sub />
                {n.monounsaturatedFatG != null && (
                  <NutRow
                    label={t("barcode.info.monofat")}
                    value={fmtG(n.monounsaturatedFatG)}
                    sub
                  />
                )}
                {n.polyunsaturatedFatG != null && (
                  <NutRow
                    label={t("barcode.info.polyfat")}
                    value={fmtG(n.polyunsaturatedFatG)}
                    sub
                  />
                )}
                <NutRow label={t("barcode.info.carbs")} value={fmtG(n.carbsG)} />
                <NutRow label={t("barcode.info.sugar")} value={fmtG(n.sugarG)} sub />
                <NutRow label={t("barcode.info.fiber")} value={fmtG(n.fiberG)} />
                <NutRow label={t("barcode.info.protein")} value={fmtG(n.proteinG)} />
                <NutRow label={t("barcode.info.salt")} value={fmtG(n.saltG)} />
              </View>
            ) : (
              <Small style={styles.muted}>{t("barcode.info.noNutrition")}</Small>
            )}

            {allergens.length > 0 && (
              <>
                <Spacer size={spacing.sm} />
                <Small style={styles.nutTitle}>{t("barcode.info.allergens")}</Small>
                <Small>{allergens}</Small>
              </>
            )}
            {mayContain.length > 0 && (
              <Small style={styles.muted}>
                {t("barcode.info.mayContain")}: {mayContain}
              </Small>
            )}

            {product.ingredientsText && (
              <>
                <Spacer size={spacing.sm} />
                <Small style={styles.nutTitle}>{t("barcode.info.ingredients")}</Small>
                <Small style={styles.muted}>{product.ingredientsText}</Small>
              </>
            )}

            <Spacer size={spacing.sm} />
            <Small style={styles.source}>
              {product.dataSource === "open_food_facts"
                ? t("barcode.info.sourceOff")
                : t("barcode.info.sourceOwn")}
            </Small>
          </ScrollView>
          <View style={styles.panelFooter}>
            {incomplete && (
              <>
                <Button label={t("barcode.complete")} onPress={() => void contributePhoto()} />
                <Spacer size={spacing.xs} />
              </>
            )}
            {/* Bekräfta + ta sig ut ur skannern (tillbaka dit man kom ifrån). */}
            <Button
              label={justContributed ? t("barcode.looksRight") : t("barcode.done")}
              onPress={() => router.back()}
            />
            <Spacer size={spacing.xs} />
            <Button label={t("barcode.info.scanAgain")} variant="ghost" onPress={scanAgain} />
          </View>
        </View>
      )}

      {/* Okänd produkt → erbjud att fota förpackningen (bidra till delade katalogen) */}
      {unknown && !contributing && !errorMsg && (
        <View style={styles.panel}>
          <View style={{ padding: spacing.lg }}>
            <Heading>{t("barcode.unknownTitle")}</Heading>
            <Body muted>{t("barcode.unknownBody")}</Body>
            <Spacer size={spacing.md} />
            <Button label={t("barcode.photoPackage")} onPress={() => void contributePhoto()} />
            <Spacer size={spacing.xs} />
            <Button label={t("barcode.info.scanAgain")} variant="ghost" onPress={scanAgain} />
          </View>
        </View>
      )}

      {/* Läser av fotad förpackning */}
      {contributing && (
        <View style={styles.panel}>
          <View style={{ padding: spacing.lg }}>
            <Heading>{t("barcode.contributing.title")}</Heading>
            <Body muted>{t("barcode.contributing.body")}</Body>
          </View>
        </View>
      )}

      {/* Fel */}
      {errorMsg && (
        <View style={styles.panel}>
          <View style={{ padding: spacing.lg }}>
            <Heading>{t("common.oops")}</Heading>
            <Body muted>{errorMsg}</Body>
            <Spacer size={spacing.md} />
            <Button label={t("barcode.info.scanAgain")} onPress={scanAgain} />
          </View>
        </View>
      )}
    </View>
  );
}

function NutRow({
  label,
  value,
  strong = false,
  sub = false,
}: {
  label: string;
  value: string;
  strong?: boolean;
  sub?: boolean;
}) {
  return (
    <Row style={[styles.nutRow, sub && styles.nutRowSub]}>
      <Small style={sub ? styles.muted : undefined}>{label}</Small>
      <Small style={strong ? styles.strong : undefined}>{value}</Small>
    </Row>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: "#000" },
  overlay: {
    position: "absolute",
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    alignItems: "center",
    justifyContent: "center",
    gap: 16,
  },
  frame: {
    width: 260,
    height: 160,
    borderWidth: 3,
    borderColor: colors.primary,
    borderRadius: 16,
    backgroundColor: "transparent",
  },
  hint: { color: "#fff" },
  panel: {
    position: "absolute",
    left: 0,
    right: 0,
    bottom: 0,
    maxHeight: "72%",
    backgroundColor: colors.surface,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
  },
  panelFooter: {
    padding: spacing.md,
    borderTopWidth: 1,
    borderTopColor: colors.border,
  },
  nutBox: {
    backgroundColor: colors.surfaceAlt,
    borderRadius: 12,
    padding: spacing.md,
  },
  nutTitle: { fontWeight: "700", marginBottom: spacing.xs, color: colors.textMuted },
  nutRow: { justifyContent: "space-between", paddingVertical: 3 },
  nutRowSub: { paddingLeft: spacing.md },
  muted: { color: colors.textMuted },
  strong: { fontWeight: "700" },
  source: { color: colors.textMuted, fontStyle: "italic" },
  savedBanner: {
    backgroundColor: colors.primarySoft,
    borderRadius: 10,
    paddingVertical: spacing.xs,
    paddingHorizontal: spacing.sm,
    marginBottom: spacing.sm,
  },
  savedText: { color: colors.primary, fontWeight: "700" },
});
