/* 彩云间 — 拍照 / 识别中动画 / 结果页 / 云详情 */

function canvasToDataUrl(cv, quality, fallback) {
  try { return cv.toDataURL('image/jpeg', quality); } catch (e) { return fallback || null; }
}

function readJpegOrientation(buffer) {
  const view = new DataView(buffer);
  if (view.byteLength < 4 || view.getUint16(0, false) !== 0xffd8) return 1;
  let offset = 2;
  while (offset + 4 < view.byteLength) {
    const marker = view.getUint16(offset, false);
    offset += 2;
    if (marker === 0xffe1) {
      const length = view.getUint16(offset, false);
      const exif = offset + 2;
      if (exif + 10 > view.byteLength) return 1;
      if (view.getUint32(exif, false) !== 0x45786966) return 1;
      const tiff = exif + 6;
      const little = view.getUint16(tiff, false) === 0x4949;
      const firstIfd = tiff + view.getUint32(tiff + 4, little);
      const entries = view.getUint16(firstIfd, little);
      for (let i = 0; i < entries; i++) {
        const entry = firstIfd + 2 + i * 12;
        if (entry + 10 > view.byteLength) return 1;
        if (view.getUint16(entry, little) === 0x0112) return view.getUint16(entry + 8, little);
      }
      return 1;
    }
    if ((marker & 0xff00) !== 0xff00) break;
    offset += view.getUint16(offset, false);
  }
  return 1;
}

function drawOrientedImage(img, orientation, max, quality, fallback) {
  const swap = orientation >= 5 && orientation <= 8;
  const srcW = img.width;
  const srcH = img.height;
  const outW = swap ? srcH : srcW;
  const outH = swap ? srcW : srcH;
  const scale = Math.min(1, max / Math.max(outW, outH));
  const cv = document.createElement('canvas');
  cv.width = Math.round(outW * scale);
  cv.height = Math.round(outH * scale);
  const ctx = cv.getContext('2d');
  ctx.save();
  ctx.scale(scale, scale);
  switch (orientation) {
    case 2: ctx.translate(outW, 0); ctx.scale(-1, 1); break;
    case 3: ctx.translate(outW, outH); ctx.rotate(Math.PI); break;
    case 4: ctx.translate(0, outH); ctx.scale(1, -1); break;
    case 5: ctx.rotate(0.5 * Math.PI); ctx.scale(1, -1); break;
    case 6: ctx.translate(outW, 0); ctx.rotate(0.5 * Math.PI); break;
    case 7: ctx.translate(outW, outH); ctx.rotate(0.5 * Math.PI); ctx.scale(-1, 1); break;
    case 8: ctx.translate(0, outH); ctx.rotate(-0.5 * Math.PI); break;
  }
  ctx.drawImage(img, 0, 0);
  ctx.restore();
  return canvasToDataUrl(cv, quality, fallback);
}

// 把图片按 EXIF 方向转正，并缩放到合理尺寸再用于识别/展示
function normalizeImageFile(file, max = 1280, quality = 0.82) {
  return new Promise(resolve => {
    if (!file) return resolve(null);
    const fallback = () => file.arrayBuffer().then(buffer => {
      const orientation = readJpegOrientation(buffer);
      const blob = new Blob([buffer], { type: file.type || 'image/jpeg' });
      const url = URL.createObjectURL(blob);
      const img = new Image();
      img.onload = () => {
        const dataUrl = drawOrientedImage(img, orientation, max, quality);
        URL.revokeObjectURL(url);
        resolve(dataUrl);
      };
      img.onerror = () => { URL.revokeObjectURL(url); resolve(null); };
      img.src = url;
    }).catch(() => resolve(null));

    if (!window.createImageBitmap) return fallback();
    createImageBitmap(file, { imageOrientation: 'from-image' }).then(bitmap => {
      const scale = Math.min(1, max / Math.max(bitmap.width, bitmap.height));
      const cv = document.createElement('canvas');
      cv.width = Math.round(bitmap.width * scale);
      cv.height = Math.round(bitmap.height * scale);
      cv.getContext('2d').drawImage(bitmap, 0, 0, cv.width, cv.height);
      if (bitmap.close) bitmap.close();
      resolve(canvasToDataUrl(cv, quality));
    }).catch(fallback);
  });
}

// 把 dataURL 缩放到合理尺寸。主要用于不支持 createImageBitmap 的浏览器兜底。
function downscaleDataUrl(dataUrl, max = 1280, quality = 0.82) {
  return new Promise(resolve => {
    if (!dataUrl) return resolve(dataUrl);
    const img = new Image();
    img.onload = () => {
      const scale = Math.min(1, max / Math.max(img.width, img.height));
      if (scale >= 1) return resolve(dataUrl); // 本来就不大，原样返回
      const cv = document.createElement('canvas');
      cv.width = Math.round(img.width * scale);
      cv.height = Math.round(img.height * scale);
      cv.getContext('2d').drawImage(img, 0, 0, cv.width, cv.height);
      resolve(canvasToDataUrl(cv, quality, dataUrl));
    };
    img.onerror = () => resolve(dataUrl);
    img.src = dataUrl;
  });
}

function skyGradient(tone) {
  switch (tone) {
    case 'storm':
    case 'rain':   return 'linear-gradient(180deg,#7d8a99,#9aa7b5 55%,#b8c2cc)';
    case 'sunset': return 'linear-gradient(180deg,#5b4a7a,#d4738a 45%,#f3a85f 80%,#f6cf86)';
    case 'night':  return 'linear-gradient(180deg,#0e1b33,#1c3052 60%,#33507a)';
    case 'pearl':  return 'linear-gradient(180deg,#9fb6d6,#c9d6e6 55%,#e7e1ec)';
    default:       return 'linear-gradient(180deg,#7fb6e4,#aed4f2 55%,#dceffb)';
  }
}

// 取景框中的天空（含目标云）
function SkyView({ cloud, children }) {
  const t = cloud.art.tone;
  const sunWarm = t === 'sunset';
  return (
    <div style={{ position:'absolute', inset:0, background: skyGradient(t), overflow:'hidden' }}>
      {t !== 'storm' && t !== 'rain' && (
        <div style={{ position:'absolute', top: sunWarm ? '52%':'14%', right: sunWarm?'30%':'18%',
          width:56, height:56, borderRadius:99,
          background: t==='night' ? 'radial-gradient(circle,#eef6ff,#cfe0f5)' : 'radial-gradient(circle at 40% 38%,#fff7df,#ffe294)',
          boxShadow: t==='night' ? '0 0 30px 8px #cfe0f555' : '0 0 40px 14px #ffe29455' }}/>
      )}
      <div style={{ position:'absolute', left:'-12%', top:'16%', width:'52%', height:'34%', opacity:.85 }}>
        <CloudArt shape="wisp" tone={t==='night'?'night':'ice'} style={{ width:'100%', height:'100%' }} soft/>
      </div>
      <div style={{ position:'absolute', left:'50%', top:'50%', transform:'translate(-50%,-50%)', width:'78%', height:'48%' }}>
        <CloudArt shape={cloud.art.shape} tone={cloud.art.tone} style={{ width:'100%', height:'100%' }}/>
      </div>
      {children}
    </div>
  );
}

// ── 拍照取景（支持真实相机 / 相册选图 / 无相机时水彩占位）──────
function CameraScreen({ fallbackCloud, onShutter, onClose }) {
  const [snap, setSnap] = React.useState(false);
  const [camReady, setCamReady] = React.useState(false);
  const videoRef = React.useRef(null);
  const streamRef = React.useRef(null);
  const fileRef = React.useRef(null);

  React.useEffect(() => {
    let stream;
    const md = navigator.mediaDevices;
    if (md && md.getUserMedia) {
      md.getUserMedia({ video: { facingMode: { ideal: 'environment' } }, audio: false })
        .then(s => {
          stream = s; streamRef.current = s;
          if (videoRef.current) { videoRef.current.srcObject = s; videoRef.current.play().catch(()=>{}); }
          setCamReady(true);
        })
        .catch(() => setCamReady(false));
    }
    return () => { if (stream) stream.getTracks().forEach(t => t.stop()); };
  }, []);

  const grabFrame = () => {
    const v = videoRef.current;
    if (!camReady || !v || !v.videoWidth) return null;
    const srcW = v.videoWidth;
    const srcH = v.videoHeight;
    const max = 900;
    const scale = Math.min(1, max / Math.max(srcW, srcH));
    const cv = document.createElement('canvas');
    cv.width = Math.round(srcW * scale);
    cv.height = Math.round(srcH * scale);
    const ctx = cv.getContext('2d');
    ctx.drawImage(v, 0, 0, cv.width, cv.height);
    return canvasToDataUrl(cv, 0.82);
  };

  const fire = () => {
    if (snap) return;
    const photo = grabFrame();        // 真实相机 → dataURL；无相机 → null（走模拟）
    setSnap(true);
    setTimeout(() => onShutter(photo), 280);
  };

  const onPick = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    normalizeImageFile(f, 1280, 0.82).then(onShutter);
  };

  return (
    <div style={{ position:'absolute', inset:0, background:'#000', overflow:'hidden' }}>
      {/* 背景：真实相机画面，否则水彩天空占位 */}
      <video ref={videoRef} playsInline muted style={{ position:'absolute', inset:0, width:'100%', height:'100%',
        objectFit:'cover', display: camReady ? 'block' : 'none' }}/>
      {!camReady && <SkyView cloud={fallbackCloud}/>}

      {/* 快门白闪 */}
      <div style={{ position:'absolute', inset:0, background:'#fff', opacity: snap?1:0,
        transition:'opacity .25s', pointerEvents:'none', zIndex:30 }}/>
      {/* 取景框 */}
      <div style={{ position:'absolute', inset:'18% 12% 24%', zIndex:10, pointerEvents:'none' }}>
        {[['top','left'],['top','right'],['bottom','left'],['bottom','right']].map((c,i)=>(
          <div key={i} style={{ position:'absolute', [c[0]]:0, [c[1]]:0, width:30, height:30,
            [`border${c[0][0].toUpperCase()+c[0].slice(1)}`]:'3px solid rgba(255,255,255,.9)',
            [`border${c[1][0].toUpperCase()+c[1].slice(1)}`]:'3px solid rgba(255,255,255,.9)' }}/>
        ))}
        <div style={{ position:'absolute', top:'50%', left:'50%', transform:'translate(-50%,-50%)',
          color:'rgba(255,255,255,.92)', fontSize:13.5, fontWeight:600, textShadow:'0 1px 8px rgba(0,0,0,.3)', whiteSpace:'nowrap' }}>
          把云朵对准取景框
        </div>
      </div>
      {/* 顶栏 */}
      <div style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 14px)', left:0, right:0, padding:'0 20px', display:'flex',
        alignItems:'center', justifyContent:'space-between', zIndex:20 }}>
        <button onClick={onClose} className="cy-tap" style={{ width:40, height:40, borderRadius:99, border:'none', cursor:'pointer',
          background:'rgba(0,0,0,.28)', color:'#fff', display:'grid', placeItems:'center', backdropFilter:'blur(6px)' }}>
          <Icon name="close" size={22}/>
        </button>
        <div style={{ background:'rgba(0,0,0,.28)', color:'#fff', fontSize:13, padding:'7px 14px', borderRadius:99, backdropFilter:'blur(6px)' }}>
          {camReady ? '对准天空中的云' : '示意天空 · 拍下即可识别'}
        </div>
        <div style={{ width:40 }}/>
      </div>
      {/* 快门 + 相册 */}
      <div style={{ position:'absolute', bottom:'calc(env(safe-area-inset-bottom) + 28px)', left:0, right:0, display:'flex', alignItems:'center', justifyContent:'center', zIndex:20 }}>
        <button onClick={fire} className="cy-tap" aria-label="拍摄" style={{ width:76, height:76, borderRadius:99, border:'none', cursor:'pointer',
          background:'rgba(255,255,255,.25)', display:'grid', placeItems:'center', boxShadow:'0 0 0 4px rgba(255,255,255,.55)' }}>
          <div style={{ width:60, height:60, borderRadius:99, background:'#fff' }}/>
        </button>
        <button onClick={() => fileRef.current && fileRef.current.click()} className="cy-tap" aria-label="从相册选择"
          style={{ position:'absolute', right:34, width:50, height:50, borderRadius:16, border:'none', cursor:'pointer',
          background:'rgba(0,0,0,.32)', color:'#fff', display:'grid', placeItems:'center', backdropFilter:'blur(6px)' }}>
          <Icon name="book" size={22}/>
        </button>
        <input ref={fileRef} type="file" accept="image/*" onChange={onPick} style={{ display:'none' }}/>
      </div>
    </div>
  );
}

// ── 识别中（趣味等待 + 真实/模拟识别）─────────────────────
const RECOG_STEPS = ['正在仰望天空…', '拨开层层云雾…', '比对《云彩手册》…', '请教一下气象局…', '马上就好…'];
function RecognizingScreen({ photo, fallbackCloud, collected, onDone }) {
  const [step, setStep] = React.useState(0);
  React.useEffect(() => {
    const iv = setInterval(() => setStep(s => Math.min(s+1, RECOG_STEPS.length-1)), 620);
    // 同时跑"识别"和"最短趣味等待"，两者都好了再出结果
    const minWait = new Promise(r => setTimeout(r, 2800));
    const recog = window.CloudRecognizer.identify(photo, { collected })
      .catch(() => ({ failed: true, source: 'api', reason: '识别服务刚才没有接住这张照片，请再试一次。' }));
    let alive = true;
    Promise.all([recog, minWait]).then(([res]) => { if (alive) onDone(res); });
    return () => { alive = false; clearInterval(iv); };
  }, []);
  return (
    <div style={{ position:'absolute', inset:0 }}>
      {photo
        ? <div style={{ position:'absolute', inset:0, overflow:'hidden' }}>
            <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'cover', backgroundPosition:'center', filter:'blur(26px) brightness(.85)', transform:'scale(1.15)' }}/>
            <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'contain', backgroundPosition:'center', backgroundRepeat:'no-repeat' }}/>
          </div>
        : <SkyView cloud={fallbackCloud}/>}
      {/* 柔光遮罩 */}
      <div style={{ position:'absolute', inset:0, background:'rgba(20,40,60,.18)', backdropFilter:'blur(1px)' }}/>
      {/* 扫描雷达 */}
      <div style={{ position:'absolute', left:'50%', top:'42%', transform:'translate(-50%,-50%)', width:200, height:200, zIndex:10 }}>
        <div className="cy-radar" style={{ position:'absolute', inset:0, borderRadius:99, border:'2px solid rgba(255,255,255,.5)' }}/>
        <div className="cy-radar" style={{ position:'absolute', inset:0, borderRadius:99, border:'2px solid rgba(255,255,255,.4)', animationDelay:'.9s' }}/>
        <div style={{ position:'absolute', inset:'30%', borderRadius:99, border:'2px dashed rgba(255,255,255,.6)', animation:'cy-spin 4s linear infinite' }}/>
        <div style={{ position:'absolute', left:'50%', top:'50%', transform:'translate(-50%,-50%)', color:'#fff' }}>
          <div style={{ animation:'cy-bob 1.6s ease-in-out infinite' }}><Icon name="sparkle" size={30}/></div>
        </div>
      </div>
      {/* 文案 */}
      <div style={{ position:'absolute', left:0, right:0, bottom:'24%', textAlign:'center', zIndex:10, padding:'0 30px' }}>
        <div style={{ display:'inline-flex', gap:6, marginBottom:16 }}>
          {[0,1,2].map(i => <span key={i} className="cy-dot" style={{ animationDelay:`${i*0.18}s` }}/>)}
        </div>
        <div key={step} style={{ color:'#fff', fontSize:18, fontWeight:600, fontFamily:'var(--cy-serif)',
          textShadow:'0 2px 12px rgba(0,0,0,.3)', animation:'cy-fadeup .4s ease' }}>
          {RECOG_STEPS[step]}
        </div>
      </div>
    </div>
  );
}

// ── 云信息正文（结果页 & 详情页共用）─────────────────────
function CloudBody({ cloud }) {
  const blocks = [
    { k:'长什么样', icon:'eye',   v:cloud.look },
    { k:'天气预兆', icon:'sky',   v:cloud.weather },
    { k:'云间冷知识', icon:'sparkle', v:cloud.lore },
  ];
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:12 }}>
      {blocks.map(b => (
        <div key={b.k} className="cy-card" style={{ padding:'15px 17px' }}>
          <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:7, color:'var(--cy-accent)' }}>
            <Icon name={b.icon} size={17}/>
            <span style={{ fontFamily:'var(--cy-serif)', fontSize:14.5, fontWeight:700, color:'var(--cy-ink)' }}>{b.k}</span>
          </div>
          <div style={{ fontSize:14, lineHeight:1.7, color:'var(--cy-ink)', opacity:.86, textWrap:'pretty' }}>{b.v}</div>
        </div>
      ))}
    </div>
  );
}

function CloudHeroHead({ cloud }) {
  return (
    <>
      <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', gap:12 }}>
        <div>
          <div style={{ fontFamily:'var(--cy-serif)', fontSize:30, fontWeight:700, color:'var(--cy-ink)', lineHeight:1.1 }}>{cloud.name}</div>
          <div style={{ fontSize:13, color:'var(--cy-ink-soft)', marginTop:5, letterSpacing:.4, fontStyle:'italic' }}>{cloud.latin}</div>
        </div>
        <RarityTag rarity={cloud.rarity}/>
      </div>
      <div style={{ display:'flex', alignItems:'center', gap:8, marginTop:12, flexWrap:'wrap' }}>
        <span style={{ display:'inline-flex', alignItems:'center', gap:5, fontSize:12.5, color:'var(--cy-ink-soft)',
          background:'var(--cy-card-dim)', padding:'5px 11px', borderRadius:99 }}>
          <Icon name="pin" size={14}/>{cloud.height}
        </span>
      </div>
      <div style={{ fontFamily:'var(--cy-brand)', fontSize:20, color:'var(--cy-ink)', opacity:.9, marginTop:14 }}>「{cloud.tagline}」</div>
    </>
  );
}

// ── 未识别到云 ───────────────────────────────────────────
function NoCloudScreen({ photo, reason, conf, onRetake, onClose }) {
  return (
    <div style={{ position:'absolute', inset:0, background:'var(--cy-bg)', zIndex:40 }}>
      <div className="cy-scroll" style={{ height:'100%' }}>
        <div style={{ position:'relative', height:330, background:'linear-gradient(180deg,#dce8f2,#f4f8fb)', overflow:'hidden' }}>
          {photo && (
            <>
              <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'cover', backgroundPosition:'center', filter:'blur(24px) brightness(.82)', transform:'scale(1.14)' }}/>
              <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'contain', backgroundPosition:'center', backgroundRepeat:'no-repeat' }}/>
              <div style={{ position:'absolute', inset:0, background:'linear-gradient(180deg, rgba(0,0,0,.2), transparent 36%)' }}/>
            </>
          )}
          <button onClick={onClose} className="cy-tap" style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 14px)', left:18, zIndex:5, width:40, height:40, borderRadius:99,
            border:'none', cursor:'pointer', background:'rgba(255,255,255,.34)', color:'#234', display:'grid', placeItems:'center', backdropFilter:'blur(6px)' }}>
            <Icon name="close" size={22}/>
          </button>
          <div style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 20px)', right:20, zIndex:5, background:'rgba(255,255,255,.36)', backdropFilter:'blur(6px)',
            padding:'7px 13px', borderRadius:99, fontSize:12.5, color:'#234', fontWeight:600, display:'flex', alignItems:'center', gap:6 }}>
            <Icon name="eye" size={15}/> 还没遇见云
          </div>
        </div>

        <div style={{ marginTop:-24, position:'relative', background:'var(--cy-bg)', borderRadius:'26px 26px 0 0', padding:'28px 22px 140px' }}>
          <div style={{ width:104, height:76, borderRadius:24, background:'linear-gradient(180deg,#fffdf8,#edf6fc)', boxShadow:'var(--cy-shadow)', display:'grid', placeItems:'center', marginBottom:20 }}>
            <CloudArt shape="wisp" tone="ice" style={{ width:'86%', height:'86%', opacity:.5 }} soft/>
          </div>
          <div style={{ fontFamily:'var(--cy-serif)', fontSize:29, fontWeight:700, color:'var(--cy-ink)', lineHeight:1.12 }}>云朵不在画面里</div>
          <div style={{ fontFamily:'var(--cy-brand)', fontSize:19, color:'var(--cy-ink)', opacity:.86, marginTop:12 }}>「它可能飘到镜头外啦」</div>
          <div style={{ display:'flex', alignItems:'flex-start', gap:8, marginTop:18, fontSize:13.5, color:'var(--cy-ink-soft)', lineHeight:1.65 }}>
            <span style={{ flex:'0 0 auto', display:'inline-flex', alignItems:'center', gap:5, background:'var(--cy-card-dim)', padding:'4px 10px', borderRadius:99, fontSize:12 }}>
              <Icon name="sparkle" size={13}/> AI 识别
            </span>
            <span>{reason || '这张照片里没有清楚的天空和云朵，所以先不放进云图鉴。把镜头往天上挪一点，我们再一起找找它。'}</span>
          </div>
          <div className="cy-card" style={{ padding:'16px 18px', marginTop:22 }}>
            <div style={{ display:'flex', alignItems:'center', gap:8, color:'var(--cy-accent)', marginBottom:8 }}>
              <Icon name="camera" size={17}/>
              <span style={{ fontFamily:'var(--cy-serif)', fontSize:15, fontWeight:700, color:'var(--cy-ink)' }}>给云朵一点出场空间</span>
            </div>
            <div style={{ fontSize:14, lineHeight:1.7, color:'var(--cy-ink)', opacity:.86 }}>
              这朵云可能刚好溜出镜头了。把手机再抬高一点，让天空占满一些，我们再找找它。
            </div>
          </div>
        </div>
      </div>
      <div style={{ position:'absolute', bottom:0, left:0, right:0, padding:'28px 18px calc(env(safe-area-inset-bottom) + 24px)', zIndex:50,
        background:'linear-gradient(transparent, var(--cy-bg) 42%)', display:'flex', gap:12 }}>
        <button onClick={onClose} className="cy-btn-ghost cy-tap" style={{ flex:'0 0 auto', width:56, padding:0 }} aria-label="关闭">
          <Icon name="close" size={22}/>
        </button>
        <button onClick={onRetake} className="cy-btn-primary cy-tap" style={{ flex:1, height:54, fontSize:16.5 }}>
          <Icon name="camera" size={20}/> 换一张天空
        </button>
      </div>
    </div>
  );
}

function RecognitionErrorScreen({ photo, reason, onRetry, onRetake, onClose }) {
  return (
    <div style={{ position:'absolute', inset:0, background:'var(--cy-bg)', zIndex:40 }}>
      <div className="cy-scroll" style={{ height:'100%' }}>
        <div style={{ position:'relative', height:330, background:'linear-gradient(180deg,#dce8f2,#f4f8fb)', overflow:'hidden' }}>
          {photo && (
            <>
              <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'cover', backgroundPosition:'center', filter:'blur(24px) brightness(.82)', transform:'scale(1.14)' }}/>
              <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'contain', backgroundPosition:'center', backgroundRepeat:'no-repeat' }}/>
              <div style={{ position:'absolute', inset:0, background:'linear-gradient(180deg, rgba(0,0,0,.2), transparent 36%)' }}/>
            </>
          )}
          <button onClick={onClose} className="cy-tap" style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 14px)', left:18, zIndex:5, width:40, height:40, borderRadius:99,
            border:'none', cursor:'pointer', background:'rgba(255,255,255,.34)', color:'#234', display:'grid', placeItems:'center', backdropFilter:'blur(6px)' }}>
            <Icon name="close" size={22}/>
          </button>
          <div style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 20px)', right:20, zIndex:5, background:'rgba(255,255,255,.36)', backdropFilter:'blur(6px)',
            padding:'7px 13px', borderRadius:99, fontSize:12.5, color:'#234', fontWeight:600, display:'flex', alignItems:'center', gap:6 }}>
            <Icon name="sparkle" size={15}/> 识别开了个小差
          </div>
        </div>

        <div style={{ marginTop:-24, position:'relative', background:'var(--cy-bg)', borderRadius:'26px 26px 0 0', padding:'28px 22px 140px' }}>
          <div style={{ width:104, height:76, borderRadius:24, background:'linear-gradient(180deg,#fffdf8,#edf6fc)', boxShadow:'var(--cy-shadow)', display:'grid', placeItems:'center', marginBottom:20 }}>
            <CloudArt shape="wisp" tone="ice" style={{ width:'86%', height:'86%', opacity:.55 }} soft/>
          </div>
          <div style={{ fontFamily:'var(--cy-serif)', fontSize:29, fontWeight:700, color:'var(--cy-ink)', lineHeight:1.12 }}>刚才没接住这朵云</div>
          <div style={{ fontFamily:'var(--cy-brand)', fontSize:19, color:'var(--cy-ink)', opacity:.86, marginTop:12 }}>「云图鉴打了个盹」</div>
          <div style={{ display:'flex', alignItems:'flex-start', gap:8, marginTop:18, fontSize:13.5, color:'var(--cy-ink-soft)', lineHeight:1.65 }}>
            <span style={{ flex:'0 0 auto', display:'inline-flex', alignItems:'center', gap:5, background:'var(--cy-card-dim)', padding:'4px 10px', borderRadius:99, fontSize:12 }}>
              <Icon name="sparkle" size={13}/> AI 识别
            </span>
            <span>{reason || '识别服务刚才没有返回结果，这张不会被记入云图鉴。可以直接再试一次。'}</span>
          </div>
          <div className="cy-card" style={{ padding:'16px 18px', marginTop:22 }}>
            <div style={{ display:'flex', alignItems:'center', gap:8, color:'var(--cy-accent)', marginBottom:8 }}>
              <Icon name="refresh" size={17}/>
              <span style={{ fontFamily:'var(--cy-serif)', fontSize:15, fontWeight:700, color:'var(--cy-ink)' }}>不用重新开始</span>
            </div>
            <div style={{ fontSize:14, lineHeight:1.7, color:'var(--cy-ink)', opacity:.86 }}>
              网络或模型偶尔会慢半拍。点“再试一次”，会用同一张照片重新识别。
            </div>
          </div>
        </div>
      </div>
      <div style={{ position:'absolute', bottom:0, left:0, right:0, padding:'28px 18px calc(env(safe-area-inset-bottom) + 24px)', zIndex:50,
        background:'linear-gradient(transparent, var(--cy-bg) 42%)', display:'flex', gap:12 }}>
        <button onClick={onClose} className="cy-btn-ghost cy-tap" style={{ flex:'0 0 auto', width:56, padding:0 }} aria-label="关闭">
          <Icon name="close" size={22}/>
        </button>
        {photo && (
          <button onClick={onRetry} className="cy-btn-primary cy-tap" style={{ flex:1, height:54, fontSize:16.5 }}>
            <Icon name="refresh" size={20}/> 再试一次
          </button>
        )}
        <button onClick={onRetake} className={photo ? 'cy-btn-ghost cy-tap' : 'cy-btn-primary cy-tap'}
          style={{ flex: photo ? '0 0 74px' : 1, height:54, fontSize: photo ? 14.5 : 16.5 }}>
          <Icon name="camera" size={photo ? 18 : 20}/> {photo ? '重拍' : '重新拍'}
        </button>
      </div>
    </div>
  );
}

// ── 识别结果 ─────────────────────────────────────────────
function ResultScreen({ cloud, isNew, conf, photo, petals = true, reason, source, onCollect, onRetake, onClose }) {
  const t = cloud.art.tone;
  const srcLabel = source === 'qwen' ? 'AI 识别' : source === 'mock' ? '演示 · 随机' : null;
  return (
    <div style={{ position:'absolute', inset:0, background:'var(--cy-bg)', zIndex:40 }}>
      <Petals show={isNew && petals}/>
      <div className="cy-scroll" style={{ height:'100%' }}>
      {/* 顶部：用户拍的照片优先，否则水彩示意 */}
      <div style={{ position:'relative', height:300, background: skyGradient(t), overflow:'hidden' }}>
        {photo && (
          <>
            <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'cover', backgroundPosition:'center', filter:'blur(26px) brightness(.82)', transform:'scale(1.15)' }}/>
            <div style={{ position:'absolute', inset:0, backgroundImage:`url(${photo})`, backgroundSize:'contain', backgroundPosition:'center', backgroundRepeat:'no-repeat' }}/>
            <div style={{ position:'absolute', inset:0, background:'linear-gradient(180deg, rgba(0,0,0,.18), transparent 30%)' }}/>
          </>
        )}
        <button onClick={onClose} className="cy-tap" style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 14px)', left:18, zIndex:5, width:40, height:40, borderRadius:99,
          border:'none', cursor:'pointer', background:'rgba(255,255,255,.3)', color:'#234', display:'grid', placeItems:'center', backdropFilter:'blur(6px)' }}>
          <Icon name="close" size={22}/>
        </button>
        <div style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 20px)', right:20, zIndex:5, background:'rgba(255,255,255,.32)', backdropFilter:'blur(6px)',
          padding:'7px 13px', borderRadius:99, fontSize:12.5, color:'#234', fontWeight:600, display:'flex', alignItems:'center', gap:6 }}>
          <Icon name="check" size={15}/> 匹配度 {conf}%
        </div>
        {photo ? (
          <div style={{ position:'absolute', left:16, bottom:16, zIndex:5, width:78, height:60, borderRadius:14, overflow:'hidden',
            background:'rgba(255,255,255,.85)', boxShadow:'0 4px 14px rgba(0,0,0,.18)', padding:4 }}>
            <CloudArt shape={cloud.art.shape} tone={cloud.art.tone} style={{ width:'100%', height:'100%' }}/>
            <div style={{ position:'absolute', bottom:2, right:4, fontSize:8.5, color:'#456', fontWeight:600 }}>图鉴示意</div>
          </div>
        ) : (
          <div style={{ position:'absolute', left:'50%', top:'54%', transform:'translate(-50%,-50%)', width:'82%', height:'70%' }}>
            <CloudArt shape={cloud.art.shape} tone={cloud.art.tone} style={{ width:'100%', height:'100%' }}/>
          </div>
        )}
        {isNew && (
          <div style={{ position:'absolute', top:108, left:'50%', transform:'translateX(-50%)', zIndex:6,
            background:'var(--cy-accent)', color:'#fff', fontSize:13, fontWeight:700, padding:'7px 18px', borderRadius:99,
            boxShadow:'0 6px 18px rgba(0,0,0,.18)', animation:'cy-pop .5s cubic-bezier(.2,1.4,.4,1) both', display:'flex', alignItems:'center', gap:6 }}>
            <Icon name="sparkle" size={15}/> 新收集 · 图鉴 +1
          </div>
        )}
      </div>
      {/* 内容 */}
      <div style={{ marginTop:-22, position:'relative', background:'var(--cy-bg)', borderRadius:'26px 26px 0 0', padding:'24px 18px 130px' }}>
        <CloudHeroHead cloud={cloud}/>
        {srcLabel && (
          <div style={{ display:'flex', alignItems:'center', gap:6, marginTop:12, fontSize:12, color:'var(--cy-ink-soft)' }}>
            <span style={{ display:'inline-flex', alignItems:'center', gap:5, background:'var(--cy-card-dim)', padding:'4px 10px', borderRadius:99 }}>
              <Icon name={source==='qwen'?'sparkle':'eye'} size={13}/>{srcLabel}
            </span>
            {reason ? <span style={{ flex:1, lineHeight:1.4 }}>{reason}</span> : null}
          </div>
        )}
        <div style={{ height:20 }}/>
        <CloudBody cloud={cloud}/>
      </div>
      </div>
      {/* 底部操作（固定不随滚动） */}
      <div style={{ position:'absolute', bottom:0, left:0, right:0, padding:'28px 18px calc(env(safe-area-inset-bottom) + 24px)', zIndex:50,
        background:'linear-gradient(transparent, var(--cy-bg) 42%)', display:'flex', gap:12 }}>
        <button onClick={onRetake} className="cy-btn-ghost cy-tap" style={{ flex:'0 0 auto', width:56, padding:0 }} aria-label="再拍一张">
          <Icon name="refresh" size={22}/>
        </button>
        <button onClick={onCollect} className="cy-btn-primary cy-tap" style={{ flex:1, height:54, fontSize:16.5 }}>
          {isNew ? <><Icon name="book" size={20}/> 收入云图鉴</> : <><Icon name="check" size={20}/> 已在图鉴中 · 完成</>}
        </button>
      </div>
    </div>
  );
}

// ── 云详情（从图鉴 / 首页进入）──────────────────────────
function DetailScreen({ cloud, records, onClose }) {
  const t = cloud.art.tone;
  const mine = (records || []).filter(r => r.id === cloud.id).sort((a, b) => b.ts - a.ts);
  const fmtDate = ts => { const d = new Date(ts); return `${d.getMonth()+1}.${d.getDate()}`; };
  return (
    <div className="cy-scroll" style={{ position:'absolute', inset:0, background:'var(--cy-bg)', zIndex:40 }}>
      <div style={{ position:'relative', height:280, background: skyGradient(t), overflow:'hidden' }}>
        <button onClick={onClose} className="cy-tap" style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 14px)', left:18, zIndex:5, width:40, height:40, borderRadius:99,
          border:'none', cursor:'pointer', background:'rgba(255,255,255,.3)', color:'#234', display:'grid', placeItems:'center', backdropFilter:'blur(6px)' }}>
          <Icon name="back" size={22}/>
        </button>
        <div style={{ position:'absolute', top:'calc(env(safe-area-inset-top) + 20px)', right:20, zIndex:5, background:'rgba(255,255,255,.32)', backdropFilter:'blur(6px)',
          padding:'7px 13px', borderRadius:99, fontSize:12.5, color:'#234', fontWeight:600, display:'flex', alignItems:'center', gap:6 }}>
          <Icon name="check" size={15}/> 已收集
        </div>
        <div style={{ position:'absolute', left:'50%', top:'54%', transform:'translate(-50%,-50%)', width:'82%', height:'70%' }}>
          <CloudArt shape={cloud.art.shape} tone={cloud.art.tone} style={{ width:'100%', height:'100%' }}/>
        </div>
      </div>
      <div style={{ marginTop:-22, position:'relative', background:'var(--cy-bg)', borderRadius:'26px 26px 0 0', padding:'24px 18px 40px' }}>
        <CloudHeroHead cloud={cloud}/>

        {/* 我拍过的这种云（按类型回顾）*/}
        {mine.length > 0 && (
          <div style={{ marginTop:20 }}>
            <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:11 }}>
              <span style={{ fontFamily:'var(--cy-serif)', fontSize:15, fontWeight:700, color:'var(--cy-ink)' }}>我拍过的 {cloud.name}</span>
              <span style={{ fontSize:12, color:'var(--cy-ink-soft)' }}>共 {mine.length} 次</span>
            </div>
            <div className="cy-hscroll" style={{ display:'flex', gap:10, overflowX:'auto', paddingBottom:4 }}>
              {mine.map((r, i) => (
                <div key={i} style={{ flex:'0 0 96px' }}>
                  <div style={{ width:96, height:96, borderRadius:14, overflow:'hidden',
                    background: r.photo ? '#000' : skyGradient(cloud.art.tone), display:'grid', placeItems:'center', boxShadow:'var(--cy-shadow)' }}>
                    {r.photo
                      ? <img src={r.photo} alt="" style={{ width:'100%', height:'100%', objectFit:'cover' }}/>
                      : <div style={{ width:'90%', height:'90%' }}><CloudArt shape={cloud.art.shape} tone={cloud.art.tone} style={{ width:'100%', height:'100%' }}/></div>}
                  </div>
                  <div style={{ fontSize:11.5, color:'var(--cy-ink-soft)', marginTop:5, textAlign:'center' }}>{fmtDate(r.ts)}</div>
                </div>
              ))}
            </div>
            {!mine.some(r => r.photo) && (
              <div style={{ fontSize:11.5, color:'var(--cy-ink-soft)', marginTop:8, lineHeight:1.5 }}>
                下次用相机拍下它，这里就会留下你自己的照片。
              </div>
            )}
          </div>
        )}

        <div style={{ height:20 }}/>
        <CloudBody cloud={cloud}/>
      </div>
    </div>
  );
}

Object.assign(window, { CameraScreen, RecognizingScreen, ResultScreen, NoCloudScreen, RecognitionErrorScreen, DetailScreen, skyGradient });
