import React, { useMemo, useState, useEffect } from "react";
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
  delayRender,
  continueRender,
  staticFile,
} from "remotion";
import { ThreeCanvas } from "@remotion/three";
import { RoundedBox, Environment, Lightformer, MeshReflectorMaterial, ContactShadows } from "@react-three/drei";
import * as THREE from "three";
import { EffectComposer, Bloom, DepthOfField, Vignette } from "@react-three/postprocessing";
import { z } from "zod";
import { colorSchema } from "../lib/branding";
import { FONT } from "../lib/fonts";
import { useLayout } from "../lib/aspect";
import { hexToRgba, mixHex } from "../lib/anim";

export const appShowcase3DSchema = z.object({
  appName: z.string(),
  tagline: z.string(),
  cta: z.string(),
  /** Optional app screenshot (textured onto the phone screen). Empty → procedural mock UI. */
  screenUrl: z.string(),
  ...colorSchema,
});

type Props = z.infer<typeof appShowcase3DSchema>;

// ── Screen texture: the user's uploaded app image, or a procedural mock UI ────
function useScreenTexture(screenUrl: string, accent: string, secondary: string): THREE.Texture {
  const procedural = useMemo(() => {
    const W = 540, H = 1170;
    const c = document.createElement("canvas");
    c.width = W; c.height = H;
    const x = c.getContext("2d")!;
    // background
    x.fillStyle = "#ffffff"; x.fillRect(0, 0, W, H);
    // status bar
    x.fillStyle = "#0f172a";
    x.font = "600 26px Arial"; x.textBaseline = "middle";
    x.textAlign = "left"; x.fillText("9:41", 40, 48);
    x.textAlign = "right";
    for (let i = 0; i < 3; i++) { x.globalAlpha = 1; x.fillRect(W - 40 - i * 26, 40, 16, 16); }
    x.globalAlpha = 1; x.textAlign = "left";
    // header band (accent)
    const hg = x.createLinearGradient(0, 90, W, 230);
    hg.addColorStop(0, accent); hg.addColorStop(1, mixHex(accent, secondary, 0.6));
    x.fillStyle = hg;
    roundRect(x, 0, 90, W, 150, 0); x.fill();
    // logo chip + title placeholder bars
    x.fillStyle = "rgba(255,255,255,0.95)"; roundRect(x, 40, 130, 70, 70, 18); x.fill();
    x.fillStyle = "rgba(255,255,255,0.95)"; roundRect(x, 130, 140, 220, 22, 11); x.fill();
    x.fillStyle = "rgba(255,255,255,0.6)"; roundRect(x, 130, 178, 150, 16, 8); x.fill();
    // hero card
    const cg = x.createLinearGradient(40, 280, W - 40, 540);
    cg.addColorStop(0, mixHex(accent, "#ffffff", 0.1)); cg.addColorStop(1, secondary);
    x.fillStyle = cg; roundRect(x, 40, 280, W - 80, 260, 28); x.fill();
    x.fillStyle = "rgba(255,255,255,0.9)"; roundRect(x, 70, 430, 200, 26, 13); x.fill();
    x.fillStyle = "rgba(255,255,255,0.6)"; roundRect(x, 70, 472, 300, 18, 9); x.fill();
    // list rows
    for (let i = 0; i < 4; i++) {
      const y = 580 + i * 110;
      x.fillStyle = "#f1f5f9"; roundRect(x, 40, y, W - 80, 90, 22); x.fill();
      x.fillStyle = accent; x.beginPath(); x.arc(95, y + 45, 28, 0, Math.PI * 2); x.fill();
      x.fillStyle = "#cbd5e1"; roundRect(x, 145, y + 24, 240, 18, 9); x.fill();
      x.fillStyle = "#e2e8f0"; roundRect(x, 145, y + 52, 160, 14, 7); x.fill();
    }
    // bottom tab bar
    x.fillStyle = "#ffffff"; x.fillRect(0, H - 110, W, 110);
    x.strokeStyle = "#e2e8f0"; x.lineWidth = 2; x.beginPath(); x.moveTo(0, H - 110); x.lineTo(W, H - 110); x.stroke();
    for (let i = 0; i < 4; i++) {
      x.fillStyle = i === 0 ? accent : "#cbd5e1";
      x.beginPath(); x.arc(80 + i * 130, H - 55, 16, 0, Math.PI * 2); x.fill();
    }
    const tex = new THREE.CanvasTexture(c);
    tex.colorSpace = THREE.SRGBColorSpace;
    tex.anisotropy = 8;
    return tex;
  }, [accent, secondary]);

  // Load the user's screenshot (if provided) as the screen texture; else procedural.
  const [imgTex, setImgTex] = useState<THREE.Texture | null>(null);
  useEffect(() => {
    if (!screenUrl) { setImgTex(null); return; }
    const handle = delayRender("load app screenshot");
    const loader = new THREE.TextureLoader();
    loader.setCrossOrigin("anonymous");
    // Full http(s) URLs (user uploads / MinIO) load directly; bare paths resolve from public/.
    const url = /^https?:\/\//.test(screenUrl) || screenUrl.startsWith("data:") ? screenUrl : staticFile(screenUrl);
    loader.load(
      url,
      (t) => { t.colorSpace = THREE.SRGBColorSpace; t.anisotropy = 8; setImgTex(t); continueRender(handle); },
      undefined,
      () => continueRender(handle),
    );
  }, [screenUrl]);

  return screenUrl && imgTex ? imgTex : procedural;
}

function roundRect(x: CanvasRenderingContext2D, X: number, Y: number, W: number, H: number, r: number) {
  x.beginPath();
  x.moveTo(X + r, Y);
  x.arcTo(X + W, Y, X + W, Y + H, r);
  x.arcTo(X + W, Y + H, X, Y + H, r);
  x.arcTo(X, Y + H, X, Y, r);
  x.arcTo(X, Y, X + W, Y, r);
  x.closePath();
}

// A rounded-rectangle screen (real phones have rounded screen corners, not square).
const RoundedScreen: React.FC<{ w: number; h: number; radius: number; z: number; tex: THREE.Texture; opacity: number; glow: string; glowOpacity: number }> = ({ w, h, radius, z, tex, opacity, glow, glowOpacity }) => {
  const geo = useMemo(() => {
    const s = new THREE.Shape();
    const x = -w / 2, y = -h / 2, r = Math.min(radius, w / 2, h / 2);
    s.moveTo(x + r, y);
    s.lineTo(x + w - r, y); s.quadraticCurveTo(x + w, y, x + w, y + r);
    s.lineTo(x + w, y + h - r); s.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    s.lineTo(x + r, y + h); s.quadraticCurveTo(x, y + h, x, y + h - r);
    s.lineTo(x, y + r); s.quadraticCurveTo(x, y, x + r, y);
    const g = new THREE.ShapeGeometry(s, 16);
    g.computeBoundingBox();
    const bb = g.boundingBox!;
    const sx = bb.max.x - bb.min.x, sy = bb.max.y - bb.min.y;
    const pos = g.attributes.position;
    const uv: number[] = [];
    for (let i = 0; i < pos.count; i++) uv.push((pos.getX(i) - bb.min.x) / sx, (pos.getY(i) - bb.min.y) / sy);
    g.setAttribute("uv", new THREE.Float32BufferAttribute(uv, 2));
    return g;
  }, [w, h, radius]);
  return (
    <group position={[0, 0, z]}>
      {glowOpacity > 0.001 && (
        <mesh geometry={geo} position={[0, 0, -0.002]}>
          <meshBasicMaterial color={glow} transparent opacity={glowOpacity} toneMapped={false} />
        </mesh>
      )}
      <mesh geometry={geo}>
        <meshBasicMaterial map={tex} toneMapped={false} transparent opacity={opacity} />
      </mesh>
    </group>
  );
};

// ── The 3D phone (generic premium flagship — Natural Titanium) ────────────────
const Phone: React.FC<{ screen: THREE.Texture; accent: string }> = ({ screen, accent }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const W = 2.02, H = 4.34, D = 0.26, rim = 0.05, bezel = 0.05;
  const TITANIUM = "#c6c3ba"; // warm natural titanium

  const enter = spring({ frame: frame - 4, fps, config: { damping: 14, stiffness: 70, mass: 0.9 } });
  const scale = interpolate(enter, [0, 1], [0.7, 1]);
  const floatY = Math.sin(frame / 34) * 0.08;
  const sway = -0.2 + Math.sin(frame / 72) * 0.1; // show the titanium side edge
  const tiltX = interpolate(enter, [0, 1], [0.45, -0.04]);
  const screenOn = interpolate(frame, [12, 42], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const btn = mixHex(TITANIUM, "#000000", 0.22);

  return (
    <group position={[0, floatY, 0]} rotation={[tiltX, sway, 0]} scale={scale}>
      {/* titanium frame */}
      <RoundedBox args={[W, H, D]} radius={0.33} smoothness={10} castShadow>
        <meshStandardMaterial color={TITANIUM} metalness={1} roughness={0.15} envMapIntensity={2.3} />
      </RoundedBox>
      {/* glossy black glass front — edge-to-edge minus a thin titanium rim */}
      <RoundedBox args={[W - rim * 2, H - rim * 2, 0.06]} radius={0.29} smoothness={10} position={[0, 0, D / 2 - 0.028]}>
        <meshPhysicalMaterial color="#05070b" metalness={0} roughness={0.1} clearcoat={1} clearcoatRoughness={0.05} envMapIntensity={1.3} />
      </RoundedBox>
      {/* rounded, textured screen + power-on glow */}
      <RoundedScreen w={W - rim * 2 - bezel * 2} h={H - rim * 2 - bezel * 2} radius={0.24} z={D / 2 + 0.006} tex={screen} opacity={screenOn} glow={accent} glowOpacity={(1 - screenOn) * 0.5} />
      {/* dynamic island + camera */}
      <group position={[0, H / 2 - rim - bezel - 0.16, D / 2 + 0.02]}>
        <RoundedBox args={[0.6, 0.15, 0.05]} radius={0.075} smoothness={6}>
          <meshStandardMaterial color="#02030a" roughness={0.25} metalness={0.2} />
        </RoundedBox>
        <mesh position={[0.2, 0, 0.03]}>
          <circleGeometry args={[0.032, 20]} />
          <meshStandardMaterial color="#0a1622" metalness={0.7} roughness={0.18} />
        </mesh>
      </group>
      {/* side buttons (titanium) */}
      <mesh position={[W / 2 + 0.006, 0.6, 0]}><boxGeometry args={[0.035, 0.52, 0.13]} /><meshStandardMaterial color={btn} metalness={1} roughness={0.3} /></mesh>
      <mesh position={[W / 2 + 0.006, -0.05, 0]}><boxGeometry args={[0.035, 0.3, 0.13]} /><meshStandardMaterial color={btn} metalness={1} roughness={0.3} /></mesh>
      <mesh position={[-W / 2 - 0.006, 0.75, 0]}><boxGeometry args={[0.035, 0.36, 0.13]} /><meshStandardMaterial color={btn} metalness={1} roughness={0.3} /></mesh>
    </group>
  );
};

const Scene: React.FC<{ accent: string; secondary: string; phoneX: number; phoneY: number; phoneScale: number; screen: THREE.Texture }> = ({ accent, secondary, phoneX, phoneY, phoneScale, screen }) => {
  const frame = useCurrentFrame();
  const orbit = Math.sin(frame / 120) * 0.05;
  return (
    <group rotation={[0, orbit, 0]}>
      {/* light, clean keynote studio */}
      <ambientLight intensity={0.7} />
      {/* key light (no harsh cast shadow — ContactShadows grounds the phone instead) */}
      <directionalLight position={[3, 6, 6]} intensity={2.2} color="#ffffff" />
      <directionalLight position={[-5, 3, 2]} intensity={0.7} color={mixHex("#ffffff", secondary, 0.3)} />
      {/* sharp rim from behind for a hot metallic edge highlight */}
      <spotLight position={[-3.5, 5, -3]} angle={0.6} penumbra={0.7} intensity={180} color="#ffffff" />
      <pointLight position={[4, -1, 4]} intensity={12} color={mixHex("#ffffff", accent, 0.4)} />
      {/* Contrasty studio env → bright softbox STREAKS on the titanium (the "shine").
          A darker fill behind the bright strips gives metal the light/dark contrast that
          reads as polished metal; the visible studio stays light (2D gradient behind canvas). */}
      <Environment resolution={512}>
        {/* light bases (keep the titanium silvery, not dark) front + back */}
        <Lightformer form="rect" intensity={1.7} position={[0, 0, -6]} scale={[24, 24, 1]} color="#c3cad6" />
        <Lightformer form="rect" intensity={1.5} position={[0, 0, 8]} scale={[24, 24, 1]} color="#eef2f7" />
        {/* bright softbox strips → hot reflection streaks on the rounded edges */}
        <Lightformer form="rect" intensity={9} position={[-2.6, 2.4, 4]} scale={[1.0, 9, 1]} color="#ffffff" />
        <Lightformer form="rect" intensity={7} position={[2.9, 0.5, 3.5]} scale={[0.8, 8, 1]} color="#ffffff" />
        <Lightformer form="rect" intensity={3} position={[0, 6, 2]} scale={[14, 3, 1]} color="#ffffff" />
      </Environment>

      <group position={[phoneX, phoneY, 0]} scale={phoneScale}>
        <Phone screen={screen} accent={accent} />
      </group>

      {/* soft contact shadow grounds the phone cleanly */}
      <ContactShadows position={[phoneX, -2.5 + phoneY * 0.05, 0]} scale={11} blur={2.8} opacity={0.32} far={6} color="#1e293b" />
      {/* subtle reflective light floor for the keynote feel */}
      <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -2.5, 0]}>
        <planeGeometry args={[40, 40]} />
        <MeshReflectorMaterial blur={[300, 90]} resolution={1024} mixBlur={1.2} mixStrength={6} roughness={0.95} depthScale={0.5} minDepthThreshold={0.5} maxDepthThreshold={1.2} color="#e9ebf0" metalness={0.1} />
      </mesh>
    </group>
  );
};

const StoreBadge: React.FC<{ kind: "ios" | "android"; L: ReturnType<typeof useLayout> }> = ({ kind, L }) => (
  <div style={{ display: "flex", alignItems: "center", gap: L.vmin(8), background: "#0f172a", color: "#fff", borderRadius: L.vmin(12), padding: `${L.vmin(8)}px ${L.vmin(16)}px` }}>
    <div style={{ width: L.vmin(26), height: L.vmin(26), borderRadius: "50%", background: "#fff", opacity: 0.95 }} />
    <div style={{ textAlign: "left", lineHeight: 1.1 }}>
      <div style={{ fontSize: L.vmin(12), opacity: 0.8 }}>{kind === "ios" ? "Download on the" : "GET IT ON"}</div>
      <div style={{ fontSize: L.vmin(19), fontWeight: 700 }}>{kind === "ios" ? "App Store" : "Google Play"}</div>
    </div>
  </div>
);

// ── Finishing layers ─────────────────────────────────────────────────────────
const LightSweep: React.FC = () => {
  const f = useCurrentFrame();
  const { width } = useVideoConfig();
  const x = interpolate(f, [26, 72], [-width * 0.35, width * 0.6], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const op = interpolate(f, [26, 40, 64, 76], [0, 0.45, 0.45, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  return (
    <AbsoluteFill style={{ pointerEvents: "none", overflow: "hidden" }}>
      <div style={{ position: "absolute", top: "-20%", height: "140%", width: 220, transform: `translateX(${x}px) rotate(12deg)`, background: "linear-gradient(90deg, transparent, rgba(255,255,255,0.7), transparent)", filter: "blur(34px)", opacity: op, mixBlendMode: "screen" }} />
    </AbsoluteFill>
  );
};

const Grain: React.FC = () => {
  const f = useCurrentFrame();
  return (
    <AbsoluteFill style={{ pointerEvents: "none", opacity: 0.045, mixBlendMode: "overlay" }}>
      <svg width="100%" height="100%">
        <filter id="appGrain">
          <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed={f % 73} stitchTiles="stitch" />
        </filter>
        <rect width="100%" height="100%" filter="url(#appGrain)" />
      </svg>
    </AbsoluteFill>
  );
};

export const AppShowcase3D: React.FC<Props> = ({
  appName, tagline, cta, screenUrl, accentColor, secondaryColor, backgroundColor, textColor,
}) => {
  const frame = useCurrentFrame();
  const { width, height, fps } = useVideoConfig();
  const L = useLayout();
  const screen = useScreenTexture(screenUrl, accentColor, secondaryColor);

  // Per-aspect re-flow: wide → phone left + text right; square/tall → phone up, text below.
  const phoneX = L.pick(-1.85, 0, 0);
  const phoneY = L.pick(0.1, 1.05, 0.55);     // raise in square/tall so the phone clears the text
  const phoneScale = L.pick(1.18, 0.8, 0.9);  // shrink in square/tall so the whole device fits
  const wide = L.isWide;

  // Text reveals.
  const nameSp = spring({ frame: frame - 70, fps, config: { damping: 13, stiffness: 90 } });
  const nameY = interpolate(nameSp, [0, 1], [L.vmin(40), 0]);
  const nameOp = interpolate(frame, [70, 90], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const tagOp = interpolate(frame, [92, 112], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const ctaSp = spring({ frame: frame - 116, fps, config: { damping: 12, stiffness: 110 } });
  const ctaScale = interpolate(ctaSp, [0, 1], [0.6, 1]);
  const ctaOp = interpolate(frame, [116, 134], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const badgeOp = interpolate(frame, [134, 152], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });

  return (
    <AbsoluteFill style={{ fontFamily: FONT, direction: "rtl", backgroundColor }}>
      {/* light studio: soft vertical gradient + faint accent glow behind the phone */}
      <AbsoluteFill style={{ background: `linear-gradient(180deg, #fbfbfd 0%, ${backgroundColor} 52%, ${mixHex(backgroundColor, "#000000", 0.09)} 100%)` }} />
      <AbsoluteFill style={{ background: `radial-gradient(circle at ${L.pick("29%", "50%", "50%")} ${L.pick("48%", "38%", "32%")}, ${hexToRgba(accentColor, 0.2)} 0%, transparent 46%)` }} />

      <ThreeCanvas
        width={width}
        height={height}
        camera={{ position: [0, 0.2, 8.2], fov: 42 }}
        shadows
        style={{ position: "absolute", inset: 0 }}
        gl={{ toneMapping: THREE.ACESFilmicToneMapping, antialias: true }}
      >
        <Scene accent={accentColor} secondary={secondaryColor} phoneX={phoneX} phoneY={phoneY} phoneScale={phoneScale} screen={screen} />
        <EffectComposer>
          <Bloom intensity={0.28} luminanceThreshold={0.75} luminanceSmoothing={0.3} mipmapBlur />
          <DepthOfField focusDistance={0.012} focalLength={0.05} bokehScale={2.5} />
          <Vignette eskil={false} offset={0.3} darkness={0.35} />
        </EffectComposer>
      </ThreeCanvas>

      <LightSweep />

      {/* text + CTA + badges — layout in LTR (predictable), text rendered RTL */}
      <div
        style={{
          position: "absolute",
          direction: "ltr",
          display: "flex",
          flexDirection: "column",
          gap: L.vmin(6),
          ...(wide
            ? { top: 0, bottom: 0, right: width * 0.05, width: width * 0.4, justifyContent: "center", alignItems: "flex-end" }
            : { left: 0, right: 0, bottom: L.vmin(80), alignItems: "center", paddingInline: L.vmin(50) }),
        }}
      >
        <div style={{ direction: "rtl", textAlign: wide ? "right" : "center", transform: `translateY(${nameY}px)`, opacity: nameOp, fontWeight: 900, fontSize: L.pick(L.vmin(82), L.vmin(78), L.vmin(72)), color: textColor, lineHeight: 1.05, maxWidth: wide ? "100%" : width * 0.86 }}>
          {appName}
        </div>
        <div style={{ direction: "rtl", textAlign: wide ? "right" : "center", opacity: tagOp, fontWeight: 500, fontSize: L.pick(L.vmin(30), L.vmin(29), L.vmin(27)), color: hexToRgba(textColor, 0.68), maxWidth: wide ? "100%" : width * 0.82 }}>
          {tagline}
        </div>
        <div style={{ direction: "rtl", marginTop: L.vmin(24), opacity: ctaOp, transform: `scale(${ctaScale})`, padding: `${L.vmin(16)}px ${L.vmin(42)}px`, borderRadius: 999, background: `linear-gradient(135deg, ${accentColor}, ${secondaryColor})`, color: "#fff", fontWeight: 800, fontSize: L.vmin(28), boxShadow: `0 ${L.vmin(8)}px ${L.vmin(24)}px ${hexToRgba(accentColor, 0.35)}` }}>
          {cta}
        </div>
        <div style={{ marginTop: L.vmin(16), opacity: badgeOp, display: "flex", gap: L.vmin(12) }}>
          <StoreBadge kind="ios" L={L} />
          <StoreBadge kind="android" L={L} />
        </div>
      </div>

      <Grain />
    </AbsoluteFill>
  );
};
