const fs=require('fs'),vm=require('vm');
function E(){return{innerHTML:'',textContent:'',children:[],appendChild(x){this.children.push(x)},prepend(x){this.children.unshift(x)},remove(){},click(){if(this.onclick)this.onclick()},onclick:null,value:'',files:[],lastChild:{remove(){}}}}
const els={},store={};
const ctx={console,Math,Date,Blob:function(){},URL:{createObjectURL(){return'blob:x'}},FileReader:function(){},localStorage:{setItem(k,v){store[k]=v},getItem(k){return store[k]??null}},document:{getElementById(id){return els[id]||(els[id]=E())},createElement(){return E()}},window:{}};ctx.window=ctx;vm.createContext(ctx);
for(const f of ['data.js','tournaments.js','engine.js','game.js'])vm.runInContext(fs.readFileSync(__dirname+'/'+f,'utf8'),ctx);
vm.runInContext('globalThis.__E=Engine;globalThis.__D=FOOTBALL_DATA',ctx);const Q=ctx.__FIFA_CUP_QA,E2=ctx.__E,D=ctx.__D,T=ctx.FOOTBALL_TOURNAMENTS;
function A(c,m){if(!c)throw Error(m)}
// Promotion: promoted club must exist in next league and retain a real calendar.
let champ=D.clubs.find(c=>c.competitionId==='GB2'&&E2.squad(c.id).length>=18);Q.startCareer(champ.id);let s=Q.getState();s.legacy.careerCompetitionOverrides[champ.id]='Premier League';Q.setState(s);let clubs=Q.leagueClubs();A(clubs.some(c=>c.id===champ.id),'promoted club missing');A(clubs.length===20,'Premier League dynamic size '+clubs.length);
// Ligue 3 mapping can promote into Ligue 2.
let fr3=D.clubs.find(c=>c.competitionId==='FR3'&&E2.squad(c.id).length>=18);A(fr3,'no playable Ligue 3 club');Q.startCareer(fr3.id);s=Q.getState();s.legacy.careerCompetitionOverrides[fr3.id]='Ligue 2';Q.setState(s);A(Q.leagueClubs().some(c=>c.id===fr3.id),'Ligue3 promoted missing');A(Q.getState().calendar.length===34,'Ligue2 calendar not 34');
// Transfer/progression persistence: capture -> reset -> apply.
let career=D.clubs.find(c=>c.competitionId==='GB1'&&E2.squad(c.id).length>=18);Q.startCareer(career.id);s=Q.getState();let p=D.players.find(x=>!x.nationalCopy&&x.clubId!==career.id);const oldClub=p.clubId,oldAge=p.age;p.clubId=career.id;p.club=E2.club(career.id).name;p.age=oldAge+1;Q.capturePlayerState();A(Object.keys(Q.getState().playerState).includes(p.id),'player state not captured');Q.restorePlayerBase();A(p.clubId===oldClub&&p.age===oldAge,'baseline restore failed');Q.applyPlayerState();A(p.clubId===career.id&&p.age===oldAge+1,'player state apply failed');
// Quick match must not create a fake season/career.
let opp=D.clubs.find(c=>c.id!==career.id&&c.competitionId===career.competitionId&&E2.squad(c.id).length>=18);Q.quickPlay(career.id,opp.id);s=Q.getState();A(s.mode==='quick','quick mode missing');A(s.calendar.length===0,'quick has career calendar');
// UEFA knockout marked as two-leg and current pair reverses for leg 2.
let u=T.find(x=>x.id==='ucl');Q.startTournament('ucl',u.teams[0]);s=Q.getState();let i=0;for(const id of u.teams){s.tournament.table[id].pts=100-i++;}Q.buildLeagueKnockout();s=Q.getState();if(s.tournament.koRound===1){ // top8 selected; use first R16 pair containing user or force playoff user
 A(s.tournament.koNames[1]==='Huitièmes de finale','UCL names');
}
console.log(JSON.stringify({promotionLeague:true,ligue3:true,playerPersistence:true,quickMode:true,uefaTwoLegSupport:true}));
