const $ = id => document.getElementById(id);
const plans = [{id:'free',name:'Free',seats:1,price:'0',relay:1,maxFile:'1 Go'},{id:'solo',name:'Individuel',seats:1,price:'0,99',ttc:'1,20',relay:5,maxFile:'10 Go'},{id:'team',name:'Entreprise 10',seats:10,price:'4,99',ttc:'6,04',relay:25,maxFile:'100 Go'},{id:'business',name:'Entreprise 100',seats:100,price:'24,99',ttc:'30,24',relay:100,maxFile:'1 To'}];
let state = null;
const message = (text, error = false) => { $('message').textContent=text; $('message').classList.toggle('error',error); };
const date = value => value ? new Date(value).toLocaleDateString('fr-BE') : '—';
const gb = bytes => `${((bytes || 0)/1e9).toLocaleString('fr-BE',{maximumFractionDigits:2})} Go`;
async function api(path, body) { const res=await fetch(path,{credentials:'same-origin',...(body!==undefined?{method:'POST',headers:{'Content-Type':'application/json',...(state?.csrfToken?{'X-CSRF-Token':state.csrfToken}:{})},body:JSON.stringify(body)}:{})});const data=await res.json();if(!res.ok)throw Object.assign(new Error(data.error || 'Service indisponible.'),{status:res.status});return data; }
function el(tag,text,className){const node=document.createElement(tag);if(text!==undefined)node.textContent=text;if(className)node.className=className;return node;}
function button(text,handler,className='secondary'){const node=el('button',text,`button ${className}`);node.type='button';node.addEventListener('click',()=>run(node,handler));return node;}
async function run(node,fn){node.disabled=true;try{await fn();}catch(error){message(error.message,true);}finally{node.disabled=false;}}
function confirmAction(text){$('confirm-text').textContent=text;const dialog=$('confirm-dialog');dialog.returnValue='cancel';dialog.showModal();return new Promise(resolve=>dialog.addEventListener('close',()=>resolve(dialog.returnValue==='confirm'),{once:true}));}
function redirectStripe(url){const target=new URL(url);if(!['https://checkout.stripe.com','https://billing.stripe.com'].includes(target.origin))throw new Error('Lien de paiement refusé.');location.assign(target.href);}
async function refresh(){state=await api('/api/account/me');render();}
async function portal(){if(state?.subscription?.complimentary)throw new Error('Votre accès propriétaire est offert et ne nécessite aucun abonnement Stripe.');redirectStripe((await api('/api/billing/portal',{})).url);}
function renderPlans(){
  const root=$('plans');root.replaceChildren();$('offers').hidden=Boolean(state?.subscription?.complimentary);if(state?.subscription?.complimentary)return;
  for(const plan of plans){
    const free=plan.id==='free', paidSubscriber=Boolean(state?.subscription?.plan&&state.subscription.plan!=='free');
    const card=el('article',undefined,'price-card');card.append(el('h3',plan.name));
    const price=el('p',`${plan.price} € `,'price');price.append(el('span',free?'/ mois':'HT / mois'));
    card.append(price,el('p',free?'Sans carte bancaire. Sans limite de durée.':`${plan.ttc} € TTC avec 21 % de TVA.`,'tax'));
    const list=el('ul');for(const text of [`${plan.maxFile} maximum par fichier${['team','business'].includes(plan.id)?' · grands volumes en cours de validation':''}`,`${plan.seats} licence${plan.seats>1?'s':''} pour ${plan.seats} ordinateur${plan.seats>1?'s':''}`,free?'3 liens par mois · expiration à 24 h':'Liens sans quota mensuel · expiration jusqu’à 7 jours',`Relais de secours : ${plan.relay} Go par mois`,'Réception gratuite, sans application','Connexion directe : aucun quota mensuel de données'])list.append(el('li',text));if(['team','business'].includes(plan.id))list.append(el('li','Page de réception aux couleurs de votre entreprise'));card.append(list);
    const choose=button(free?(state?(paidSubscriber?'Free après votre abonnement':'Votre offre gratuite'):'Créer mon compte gratuit'):(paidSubscriber?'Gérer mon offre':`Choisir ${plan.name}`),async()=>{
      if(!state){$('email').focus();message(free?'Créez votre compte gratuit, sans carte bancaire.':'Créez votre compte ou connectez-vous pour choisir cette offre.');return;}
      if(free){if(paidSubscriber)message('Pour revenir à Free, annulez le renouvellement de votre abonnement dans le portail Stripe. Votre accès payant reste disponible jusqu’à sa date de fin.');return;}
      if(paidSubscriber)return portal();
      redirectStripe((await api('/api/billing/checkout',{plan:plan.id})).url);
    },'primary');
    choose.disabled=free?Boolean(state&&!paidSubscriber):Boolean(state&&!state.billingEnabled);card.append(choose);root.append(card);
  }
}
function previewBranding(){
  const accent=/^#[0-9a-f]{6}$/i.test($('branding-accent').value)?$('branding-accent').value:'#24513c';
  $('branding-preview-name').textContent=$('branding-company').value.trim()||'Votre entreprise';
  $('branding-preview-message').textContent=$('branding-message').value.trim()||'Votre message d’accueil apparaîtra ici.';
  $('branding-preview-accent').style.backgroundColor=accent;
  $('branding-accent-value').textContent=accent;
}
function renderBranding(){
  const enabled=Boolean(state?.brandingEnabled&&['team','business'].includes(state?.subscription?.plan)&&state?.subscription?.slots);
  $('branding-form').hidden=!enabled;$('branding-upgrade').hidden=enabled;
  const branding=state?.branding||{};
  $('branding-company').value=branding.companyName||'';$('branding-message').value=branding.message||'';
  $('branding-accent').value=/^#[0-9a-f]{6}$/i.test(branding.accent||'')?branding.accent:'#24513c';
  previewBranding();
}
let adminRequest=0;
async function loadAdmin(){
  const request=++adminRequest, accountId=state?.account?.id;
  if(state?.account?.role!=='admin')return;
  $('admin-status').textContent='Chargement…';
  try{
    const data=await api('/api/admin/overview');
    if(request!==adminRequest||state?.account?.id!==accountId||state?.account?.role!=='admin')return;
    $('admin-total').textContent=data.totalAccounts??data.stats?.totalAccounts??0;
    $('admin-devices').textContent=data.activeDevices??data.stats?.activeDevices??0;
    const root=$('admin-accounts');root.replaceChildren();
    for(const account of (data.accounts||[]).slice(0,50)){
      const row=el('tr');for(const value of [account.email,account.role==='admin'?'Administrateur':'Client',plans.find(p=>p.id===account.plan)?.name||account.plan||'—',account.slots??0,account.activeDevices??0,date(account.created)])row.append(el('td',String(value)));root.append(row);
    }
    $('admin-status').textContent='Les 50 comptes les plus récents au maximum. Vue en lecture seule.';
  }catch(error){if(request===adminRequest&&state?.account?.role==='admin')$('admin-status').textContent=error.message;}
}
function renderAdmin(){
  adminRequest++;const admin=state?.account?.role==='admin';$('administration').hidden=!admin;
  $('admin-accounts').replaceChildren();$('admin-total').textContent='—';$('admin-devices').textContent='—';$('admin-status').textContent='';
  if(admin)void loadAdmin();
}
function render(){ renderAdmin();$('auth').hidden=Boolean(state);$('dashboard').hidden=!state;renderPlans();if(!state)return;$('account-email').textContent=state.account.email;renderBranding();const sub=state.subscription,active=Boolean(sub?.slots),plan=plans.find(p=>p.id===sub?.plan);const free=sub?.plan==='free', complimentary=Boolean(sub?.complimentary), paid=Boolean(sub&&!free&&!complimentary);
$('subscription-status').textContent=complimentary?'Administrateur · Accès offert':free?'Free · Gratuit':active?'Actif':'Sans abonnement actif';
$('subscription-detail').textContent=complimentary?'Entreprise 100 · 100 postes · Accès offert permanent.':free?'Free · 1 poste · gratuit sans limite de durée et sans carte bancaire.':active?`${plan?.name || 'Abonnement'} · ${sub.slots} poste${sub.slots>1?'s':''} · ${sub.cancelAtPeriodEnd?'Annulation prévue, accès jusqu’au':'Période payée jusqu’au'} ${date(sub.currentPeriodEnd)}.`:'Votre accès est indisponible. Cliquez sur Actualiser.';
$('billing-notice').textContent=complimentary?'Aucun paiement ni renouvellement nécessaire pour votre accès propriétaire.':state.billingEnabled?(free?'Vous pouvez passer à une offre payante ci-dessous. Votre compte Free reste gratuit.':'Paiements, factures et gestion de l’abonnement via Stripe.'):'Free est disponible. Les offres payantes ouvriront après configuration de Stripe.';
for(const id of ['portal','cancel']){$(id).hidden=!paid;$(id).disabled=!state.billingEnabled||!paid;}
$('billing-help').hidden=!paid;
$('file-limit').textContent=state.maxFileBytes>=1e12?`${(state.maxFileBytes/1e12).toLocaleString('fr-BE')} To`:state.maxFileBytes?gb(state.maxFileBytes):(plan?.maxFile||'—');
$('transfer-count').textContent=free?`${state.transfersUsed||0} / ${state.transfersLimit??3}`:'Sans quota mensuel';
$('transfer-detail').textContent=free?`Liens créés en ${state.transferPeriod||'ce mois'} (mois civil UTC). Chaque lien expire au plus tard après 24 heures. Le compteur se renouvelle chaque mois ; un lien peut contenir plusieurs fichiers.`:'Pas de quota commercial mensuel sur les liens. Jusqu’à 10 liens actifs par poste, avec une expiration maximale de 7 jours.';
$('seat-count').textContent=sub?.slots||0;$('active-count').textContent=state.licenses.filter(l=>l.entitled&&l.status==='activated').length;$('relay-count').textContent=gb(state.relayUsedBytes);$('relay-detail').textContent=`Relais de secours : ${gb(state.relayUsedBytes)} utilisés sur ${gb(state.relayLimitBytes)} en ${state.relayPeriod || 'ce mois'}, pour tous vos ordinateurs. Il intervient seulement si la connexion directe est impossible. Une fois ce quota épuisé, les transferts qui en ont besoin s’arrêtent, sans supplément. Les connexions directes ne sont pas décomptées.`;for(const id of ['download-mac','download-win']){$(id).classList.toggle('disabled-link',!active);$(id).setAttribute('aria-disabled',String(!active));$(id).tabIndex=active?0:-1;}$('no-licenses').hidden=state.licenses.length>0;const root=$('licenses');root.replaceChildren();for(const license of state.licenses){const row=el('article',undefined,'license-row');const heading=el('h3',`Ordinateur ${license.ordinal}`);const labels={activated:'Activée',not_activated:'Non activée',disabled:'Désactivée',revoked:'Révoquée'};heading.append(el('span',!license.entitled?'Hors forfait actuel':labels[license.status]||license.status,'pill'));row.append(heading,el('p',`Ordinateur : ${license.machineName || 'Aucun appareil associé'}`));const fields=el('div',undefined,'license-fields');const name=el('input'),department=el('input');name.value=license.name||'';department.value=license.department||'';name.maxLength=department.maxLength=100;name.autocomplete=department.autocomplete='off';for(const [text,input] of [['Nom de la personne ou du poste',name],['Service',department]]){const label=el('label',text);label.append(input);fields.append(label);}row.append(fields);const actions=el('div',undefined,'button-row');const mutate=async(action,body={})=>{await api(`/api/licenses/${license.id}/${action}`,body);await refresh();message('Licence mise à jour.');};actions.append(button('Enregistrer',()=>mutate('assign',{name:name.value,department:department.value})));if(license.entitled&&license.status==='not_activated')actions.append(button(sub.slots===1?'Activer mon ordinateur':'Obtenir le lien d’activation',async()=>{const invite=await api(`/api/licenses/${license.id}/activation`,{});$('activation-url').value=`${location.origin}/activate#${invite.token}`;$('invitation').hidden=false;$('invitation').scrollIntoView({behavior:'smooth',block:'center'});message(`Lien valable jusqu’au ${date(invite.expiresAt)}. Ne le publiez pas.`);},'primary'));if(license.status==='activated')actions.append(button('Changer d’ordinateur',async()=>{if(await confirmAction('Retirer cet ordinateur ? Ses partages seront arrêtés. Vous pourrez ensuite créer un nouveau lien pour le poste de remplacement.'))await mutate('reset');}));if(license.status==='disabled')actions.append(button('Réactiver la licence',()=>mutate('enable')));else if(license.status!=='revoked')actions.append(button('Désactiver la licence',async()=>{if(await confirmAction('Désactiver cette licence et arrêter ses partages en cours ? Vous pourrez la réactiver ultérieurement.'))await mutate('disable');}));row.append(actions);root.append(row);}}
$('auth-form').addEventListener('submit',event=>{event.preventDefault();const action=event.submitter?.value||'login';run(event.submitter||$('email'),async()=>{state=await api(`/api/account/${action}`,{email:$('email').value,password:$('password').value});$('password').value='';render();message(action==='register'?'Compte Free créé. Activez votre ordinateur depuis « Mes ordinateurs » pour utiliser vos 3 liens gratuits chaque mois.':'Vous êtes connecté.');});});
$('logout').addEventListener('click',()=>run($('logout'),async()=>{await api('/api/account/logout',{});state=null;$('activation-url').value='';$('invitation').hidden=true;render();message('Vous êtes déconnecté.');}));
$('admin-refresh').addEventListener('click',()=>run($('admin-refresh'),loadAdmin));
$('refresh').addEventListener('click',()=>run($('refresh'),refresh));for(const id of ['portal','cancel'])$(id).addEventListener('click',()=>run($(id),portal));$('copy-invitation').addEventListener('click',()=>run($('copy-invitation'),async()=>{await navigator.clipboard.writeText($('activation-url').value);message('Lien copié.');}));$('close-invitation').addEventListener('click',()=>{$('activation-url').value='';$('invitation').hidden=true;});
for(const id of ['branding-company','branding-message','branding-accent'])$(id).addEventListener('input',previewBranding);
$('branding-form').addEventListener('submit',event=>{event.preventDefault();run($('branding-save'),async()=>{
  await api('/api/account/branding',{companyName:$('branding-company').value.trim(),message:$('branding-message').value.trim(),accent:$('branding-accent').value});
  await refresh();message('Personnalisation enregistrée. Elle sera utilisée pour vos nouveaux liens de partage.');
});});
render();refresh().catch(error=>{if(error.status!==401)message('L’espace client est momentanément indisponible. Aucun paiement ne peut être effectué ici.',true);});
