import React from 'react';
import {AbsoluteFill, Audio, CalculateMetadataFunction, Easing, Freeze, Img, OffthreadVideo, Sequence, interpolate, staticFile, useCurrentFrame, useVideoConfig} from 'remotion';
import prepared from './full-timeline.json';
type Notification={id:string;src:string;x:number;y:number;width:number;height:number;quote:string};
type Narration={src:string;durationSeconds:number;startFrame:number;text:string};
type Clip={src:string;durationFrames:number};
type Scene={number:number;mode:string;transition:string;startFrame:number;durationInFrames:number;background:string;ui:string;chrome:string;postUi:string;notifications:Notification[];poem:string;audio:Narration|null;clip:Clip|null};
export type FullTimeline={version:number;fps:number;width:number;height:number;durationInFrames:number;shutdown:{startFrame:number;durationInFrames:number;fadeStart:number;fadeDuration:number};frames:Scene[]};
export type FullProps={timeline:FullTimeline;reducedMotion:boolean};
const easeOut=Easing.bezier(.23,1,.32,1),easeScroll=Easing.bezier(.32,.72,0,1),easeInOut=Easing.bezier(.77,0,.175,1);
const progress=(frame:number,start:number,length:number,easing=easeOut)=>interpolate(frame,[start,start+length],[0,1],{extrapolateLeft:'clamp',extrapolateRight:'clamp',easing});
const fullSize={position:'absolute' as const,width:393,height:852,left:0,top:0};
export const fullMetadata:CalculateMetadataFunction<FullProps>=async({props,abortSignal})=>{
 const response=await fetch(staticFile('full-timeline.json'),{signal:abortSignal});
 if(!response.ok)throw new Error('Run node prepare-full.cjs to prepare the film.');
 const timeline:FullTimeline=await response.json();
 return {durationInFrames:timeline.durationInFrames,fps:timeline.fps,width:timeline.width,height:timeline.height,props:{...props,timeline}};
};
const Post:React.FC<{scene:Scene}>=({scene})=><AbsoluteFill>
 {scene.clip?<OffthreadVideo src={staticFile(scene.clip.src)} muted playbackRate={scene.clip.durationFrames/scene.durationInFrames} style={{...fullSize,objectFit:'cover'}}/>:<Img src={staticFile(scene.background)} style={fullSize}/>}
 <Img src={staticFile(scene.postUi)} style={fullSize}/>
</AbsoluteFill>;
const LockScreen:React.FC<{scene:Scene;previous?:Scene;still?:boolean;reducedMotion:boolean}>=({scene,previous,still=false,reducedMotion})=>{
 const frame=useCurrentFrame(),local=still?scene.durationInFrames-1:frame;
 const old=previous?.mode==='lock'?previous.notifications:[];
 return <AbsoluteFill>
  <Img src={staticFile(scene.background)} style={fullSize}/>
  <Img src={staticFile(scene.ui)} style={fullSize}/>
  {old.filter(n=>!scene.notifications.some(next=>next.quote===n.quote)).map(n=>{
   const exit=progress(local,0,8);
   return <Img key={'exit-'+n.id} src={staticFile(n.src)} style={{position:'absolute',left:n.x,top:n.y,width:n.width,height:n.height,opacity:1-exit,transform:reducedMotion?'none':`translateY(${exit*(852-n.y+24)}px)`}}/>;
  })}
  {scene.notifications.map((n,index)=>{
   const retained=old.find(p=>p.quote===n.quote);
   const delay=scene.number===18&&index===1?38:scene.transition==='lock'?14:8+index*2;
   const enter=still?1:progress(local,delay,8),shift=progress(local,0,8);
   const translation=retained?(retained.y-n.y)*(1-shift):(852-n.y+20)*(1-enter);
   const tap=scene.number===2||scene.number===9?progress(local,scene.durationInFrames-12,4)*(1-progress(local,scene.durationInFrames-6,4)):0;
   return <Img key={n.id} src={staticFile(n.src)} style={{position:'absolute',left:n.x,top:n.y,width:n.width,height:n.height,opacity:retained||still?1:enter,transform:`translateY(${reducedMotion?0:translation}px) scale(${index===0?1-tap*.025:1})`}}/>;
  })}
  {(scene.number===2||scene.number===9)&&local>=scene.durationInFrames-12&&(()=>{
   const n=scene.notifications[0],p=progress(local,scene.durationInFrames-12,11);
   return <div style={{position:'absolute',left:n.x+n.width*.7-19,top:n.y+n.height*.6-19,width:38,height:38,borderRadius:'50%',border:'2px solid rgba(255,255,255,.8)',background:'rgba(255,255,255,.13)',opacity:1-p,transform:`scale(${.75+p*.4})`}}/>;
  })()}
 </AbsoluteFill>;
};
const CompleteScreen:React.FC<{scene:Scene;reducedMotion:boolean}>=({scene,reducedMotion})=>scene.mode==='lock'?<LockScreen scene={scene} still reducedMotion={reducedMotion}/>:<AbsoluteFill><Post scene={scene}/><Img src={staticFile(scene.chrome)} style={fullSize}/></AbsoluteFill>;
const SceneView:React.FC<{scene:Scene;previous?:Scene;reducedMotion:boolean}>=({scene,previous,reducedMotion})=>{
 const local=useCurrentFrame();
 if(scene.mode==='reels'){
  const scrolling=scene.transition==='scroll'&&previous,p=progress(local,0,reducedMotion?6:12,easeScroll);
  // Posts scroll; the status bar, header and navigation remain fixed.
  if(scrolling)return <AbsoluteFill>
   {p<1&&<div style={{...fullSize,transform:reducedMotion?'none':`translateY(${-p*852}px)`,opacity:reducedMotion?1-p:1}}><Freeze frame={previous.durationInFrames-1}><Post scene={previous}/></Freeze></div>}
   <div style={{...fullSize,transform:reducedMotion?'none':`translateY(${(1-p)*852}px)`,opacity:reducedMotion?p:1}}><Post scene={scene}/></div>
   <Img src={staticFile(scene.chrome)} style={fullSize}/>
  </AbsoluteFill>;
  const unlock=progress(local,0,8);
  return <AbsoluteFill>
   {previous&&unlock<1&&<Freeze frame={previous.durationInFrames-1}><CompleteScreen scene={previous} reducedMotion={reducedMotion}/></Freeze>}
   <div style={{...fullSize,opacity:unlock,transform:reducedMotion?'none':`scale(${.96+.04*unlock})`}}><Post scene={scene}/><Img src={staticFile(scene.chrome)} style={fullSize}/></div>
  </AbsoluteFill>;
 }
 const locking=scene.transition==='lock'&&previous,wake=locking?progress(local,6,7):1;
 // The feed goes dark briefly, then the lock screen wakes for the notification.
 return <AbsoluteFill style={{background:'#000'}}>
  <div style={{...fullSize,opacity:wake}}><LockScreen scene={scene} previous={previous} reducedMotion={reducedMotion}/></div>
  {locking&&local<6&&<div style={{...fullSize,opacity:1-progress(local,0,5)}}><Freeze frame={previous.durationInFrames-1}><CompleteScreen scene={previous} reducedMotion={reducedMotion}/></Freeze></div>}
 </AbsoluteFill>;
};
const PowerOff:React.FC<{timeline:FullTimeline;reducedMotion:boolean}>=({timeline,reducedMotion})=>{
 const frame=useCurrentFrame(),s=timeline.shutdown,last=timeline.frames[timeline.frames.length-1],fade=progress(frame,s.fadeStart,s.fadeDuration,easeInOut);
 return <AbsoluteFill style={{background:'#000'}}>{fade<1&&<div style={{...fullSize,opacity:1-fade}}><Freeze frame={last.durationInFrames-1}><LockScreen scene={last} still reducedMotion={reducedMotion}/></Freeze></div>}</AbsoluteFill>;
};
export const Full:React.FC<FullProps>=({timeline,reducedMotion})=>{
 const {width,height}=useVideoConfig();
 return <AbsoluteFill style={{backgroundColor:'#000'}}>
  <div style={{...fullSize,overflow:'hidden',transform:`scale(${width/393},${height/852})`,transformOrigin:'top left'}}>
   {timeline.frames.map((scene,i)=><Sequence key={scene.number} from={scene.startFrame} durationInFrames={scene.durationInFrames} name={`Frame ${String(scene.number).padStart(2,'0')} · ${scene.transition}`}><SceneView scene={scene} previous={timeline.frames[i-1]} reducedMotion={reducedMotion}/></Sequence>)}
   <Sequence from={timeline.shutdown.startFrame} durationInFrames={timeline.shutdown.durationInFrames} name="Phone powers off"><PowerOff timeline={timeline} reducedMotion={reducedMotion}/></Sequence>
  </div>
  {timeline.frames.filter(scene=>scene.audio).map(scene=><Sequence key={'voice-'+scene.number} from={scene.startFrame+scene.audio!.startFrame} durationInFrames={scene.durationInFrames-scene.audio!.startFrame} name={`Exact poem voice ${scene.number}`}><Audio src={staticFile(scene.audio!.src)}/></Sequence>)}
 </AbsoluteFill>;
};
export const fullDefaultProps:FullProps={timeline:prepared,reducedMotion:false};
