class CountdownTimer{constructor(options={}){this.config={name:options.name||'default_timer',duration:options.duration||1800,autoStart:options.autoStart||false,container:options.container||'countdown-timer',size:options.size||'large',theme:options.theme||'primary',showProgress:options.showProgress!==false,showControls:options.showControls!==false,warningThreshold:options.warningThreshold||30,showWarning:options.showWarning!==false,warningMessage:options.warningMessage||'Timer läuft ab!',forceQuitEnabled:options.forceQuitEnabled!==false,forceQuitAction:options.forceQuitAction||'logout',customEndpoint:options.customEndpoint||null,onTick:options.onTick||null,onWarning:options.onWarning||null,onExpired:options.onExpired||null,onForceQuit:options.onForceQuit||null,apiBase:options.apiBase||'/api/timers',updateInterval:options.updateInterval||1000,syncWithServer:options.syncWithServer!==false};this.state={remaining:this.config.duration,total:this.config.duration,status:'stopped',warningShown:false,lastServerSync:null};this.elements={};this.intervals={countdown:null,serverSync:null};this.listeners=new Map();this.init();}
init(){this.createUI();this.attachEventListeners();if(this.config.syncWithServer){this.syncWithServer();this.startServerSync();}
if(this.config.autoStart){this.start();}
console.log(`Timer'${this.config.name}'initialisiert`);}
createUI(){const container=document.getElementById(this.config.container);if(!container){console.error(`Container'${this.config.container}'nicht gefunden`);return;}
const timerWrapper=document.createElement('div');timerWrapper.className=`countdown-timer-wrapper size-${this.config.size}theme-${this.config.theme}`;timerWrapper.innerHTML=this.getTimerHTML();container.appendChild(timerWrapper);this.elements={wrapper:timerWrapper,display:timerWrapper.querySelector('.timer-display'),timeText:timerWrapper.querySelector('.time-text'),progressBar:timerWrapper.querySelector('.progress-fill'),progressText:timerWrapper.querySelector('.progress-text'),statusIndicator:timerWrapper.querySelector('.status-indicator'),warningBox:timerWrapper.querySelector('.warning-box'),warningText:timerWrapper.querySelector('.warning-text'),controls:timerWrapper.querySelector('.timer-controls'),startBtn:timerWrapper.querySelector('.btn-start'),pauseBtn:timerWrapper.querySelector('.btn-pause'),stopBtn:timerWrapper.querySelector('.btn-stop'),resetBtn:timerWrapper.querySelector('.btn-reset'),extendBtn:timerWrapper.querySelector('.btn-extend')};this.updateDisplay();}
getTimerHTML(){return`
${this.formatTime(this.state.remaining)}verbleibend
Gestoppt
${this.config.showProgress?`
`:''}
${this.config.warningMessage}
${this.config.showControls?`
`:''}
`;}
attachEventListeners(){if(this.elements.startBtn){this.elements.startBtn.addEventListener('click',()=>this.start());}
if(this.elements.pauseBtn){this.elements.pauseBtn.addEventListener('click',()=>this.pause());}
if(this.elements.stopBtn){this.elements.stopBtn.addEventListener('click',()=>this.stop());}
if(this.elements.resetBtn){this.elements.resetBtn.addEventListener('click',()=>this.reset());}
if(this.elements.extendBtn){this.elements.extendBtn.addEventListener('click',()=>this.extend(300));}
document.addEventListener('keydown',(e)=>this.handleKeyboardShortcuts(e));document.addEventListener('visibilitychange',()=>this.handleVisibilityChange());window.addEventListener('beforeunload',(e)=>this.handleBeforeUnload(e));}
async start(){try{if(this.state.status==='running'){return true;}
if(this.config.syncWithServer){const response=await this.apiCall('start','POST');if(!response.success){this.showError('Fehler beim Starten des Timers');return false;}}
this.state.status='running';this.startCountdown();this.updateControls();this.updateStatusIndicator();console.log(`Timer'${this.config.name}'gestartet`);return true;}catch(error){console.error('Fehler beim Starten des Timers:',error);this.showError('Timer konnte nicht gestartet werden');return false;}}
async pause(){try{if(this.state.status!=='running'){return true;}
if(this.config.syncWithServer){const response=await this.apiCall('pause','POST');if(!response.success){this.showError('Fehler beim Pausieren des Timers');return false;}}
this.state.status='paused';this.stopCountdown();this.updateControls();this.updateStatusIndicator();console.log(`Timer'${this.config.name}'pausiert`);return true;}catch(error){console.error('Fehler beim Pausieren des Timers:',error);this.showError('Timer konnte nicht pausiert werden');return false;}}
async stop(){try{if(this.config.syncWithServer){const response=await this.apiCall('stop','POST');if(!response.success){this.showError('Fehler beim Stoppen des Timers');return false;}}
this.state.status='stopped';this.state.remaining=this.state.total;this.state.warningShown=false;this.stopCountdown();this.hideWarning();this.updateDisplay();this.updateControls();this.updateStatusIndicator();console.log(`Timer'${this.config.name}'gestoppt`);return true;}catch(error){console.error('Fehler beim Stoppen des Timers:',error);this.showError('Timer konnte nicht gestoppt werden');return false;}}
async reset(){try{if(this.config.syncWithServer){const response=await this.apiCall('reset','POST');if(!response.success){this.showError('Fehler beim Zurücksetzen des Timers');return false;}}
this.stop();this.state.remaining=this.state.total;this.updateDisplay();console.log(`Timer'${this.config.name}'zurückgesetzt`);return true;}catch(error){console.error('Fehler beim Zurücksetzen des Timers:',error);this.showError('Timer konnte nicht zurückgesetzt werden');return false;}}
async extend(seconds){try{if(this.config.syncWithServer){const response=await this.apiCall('extend','POST',{seconds});if(!response.success){this.showError('Fehler beim Verlängern des Timers');return false;}}
this.state.remaining+=seconds;this.state.total+=seconds;this.state.warningShown=false;this.hideWarning();this.updateDisplay();this.showToast(`Timer um ${Math.floor(seconds/60)}Minuten verlängert`,'success');console.log(`Timer'${this.config.name}'um ${seconds}Sekunden verlängert`);return true;}catch(error){console.error('Fehler beim Verlängern des Timers:',error);this.showError('Timer konnte nicht verlängert werden');return false;}}
startCountdown(){this.stopCountdown();this.intervals.countdown=setInterval(()=>{this.tick();},this.config.updateInterval);}
stopCountdown(){if(this.intervals.countdown){clearInterval(this.intervals.countdown);this.intervals.countdown=null;}}
tick(){if(this.state.status!=='running'){return;}
this.state.remaining=Math.max(0,this.state.remaining-1);this.updateDisplay();if(this.config.onTick){this.config.onTick(this.state.remaining,this.state.total);}
if(!this.state.warningShown&&this.state.remaining<=this.config.warningThreshold&&this.state.remaining>0){this.showWarning();}
if(this.state.remaining<=0){this.handleExpired();}}
async handleExpired(){console.warn(`Timer'${this.config.name}'ist abgelaufen`);this.state.status='expired';this.stopCountdown();this.updateDisplay();this.updateStatusIndicator();if(this.config.onExpired){this.config.onExpired();}
if(this.config.forceQuitEnabled){await this.executeForceQuit();}}
async executeForceQuit(){try{console.warn(`Force-Quit für Timer'${this.config.name}'wird ausgeführt...`);if(this.config.onForceQuit){const shouldContinue=this.config.onForceQuit(this.config.forceQuitAction);if(!shouldContinue){return;}}
if(this.config.syncWithServer){const response=await this.apiCall('force-quit','POST');if(!response.success){console.error('Force-Quit-API-Aufruf fehlgeschlagen');}}
switch(this.config.forceQuitAction){case'logout':this.performLogout();break;case'redirect':this.performRedirect();break;case'refresh':this.performRefresh();break;case'custom':this.performCustomAction();break;default:console.warn(`Unbekannte Force-Quit-Aktion:${this.config.forceQuitAction}`);}}catch(error){console.error('Fehler bei Force-Quit-Ausführung:',error);}}
performLogout(){this.showModal('Session abgelaufen','Sie werden automatisch abgemeldet...','warning');setTimeout(()=>{window.location.href='/auth/logout';},2000);}
performRedirect(){const redirectUrl=this.config.redirectUrl||'/';this.showModal('Umleitung','Sie werden weitergeleitet...','info');setTimeout(()=>{window.location.href=redirectUrl;},2000);}
performRefresh(){this.showModal('Seite wird aktualisiert','Die Seite wird automatisch neu geladen...','info');setTimeout(()=>{window.location.reload();},2000);}
performCustomAction(){if(this.config.customEndpoint){fetch(this.config.customEndpoint,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.getCSRFToken()},body:JSON.stringify({timer_name:this.config.name,action:'force_quit'})}).catch(error=>{console.error('Custom-Action-Request fehlgeschlagen:',error);});}}
showWarning(){if(!this.config.showWarning||this.state.warningShown){return;}
this.state.warningShown=true;if(this.elements.warningBox){this.elements.warningBox.style.display='block';this.elements.warningBox.classList.add('pulse');}
if(this.config.onWarning){this.config.onWarning(this.state.remaining);}
this.showNotification('Timer-Warnung',this.config.warningMessage);console.warn(`Timer-Warnung für'${this.config.name}':${this.state.remaining}Sekunden verbleiben`);}
hideWarning(){this.state.warningShown=false;if(this.elements.warningBox){this.elements.warningBox.style.display='none';this.elements.warningBox.classList.remove('pulse');}}
updateDisplay(){if(this.elements.timeText){this.elements.timeText.textContent=this.formatTime(this.state.remaining);}
if(this.config.showProgress&&this.elements.progressBar){const progress=((this.state.total-this.state.remaining)/this.state.total)*100;this.elements.progressBar.style.width=`${progress}%`;if(this.elements.progressText){this.elements.progressText.textContent=`${Math.round(progress)}%abgelaufen`;}}
this.updateTheme();}
updateTheme(){if(!this.elements.wrapper)return;const progress=(this.state.total-this.state.remaining)/this.state.total;this.elements.wrapper.classList.remove('theme-primary','theme-warning','theme-danger');if(progress<0.7){this.elements.wrapper.classList.add('theme-primary');}else if(progress<0.9){this.elements.wrapper.classList.add('theme-warning');}else{this.elements.wrapper.classList.add('theme-danger');}}
updateControls(){if(!this.config.showControls)return;const isRunning=this.state.status==='running';const isPaused=this.state.status==='paused';const isStopped=this.state.status==='stopped';if(this.elements.startBtn){this.elements.startBtn.style.display=(isStopped||isPaused)?'inline-flex':'none';}
if(this.elements.pauseBtn){this.elements.pauseBtn.style.display=isRunning?'inline-flex':'none';}
if(this.elements.stopBtn){this.elements.stopBtn.disabled=isStopped;}
if(this.elements.resetBtn){this.elements.resetBtn.disabled=isRunning;}
if(this.elements.extendBtn){this.elements.extendBtn.disabled=this.state.status==='expired';}}
updateStatusIndicator(){if(!this.elements.statusIndicator)return;const statusText=this.elements.statusIndicator.querySelector('.status-text');const statusIcon=this.elements.statusIndicator.querySelector('i');if(statusText&&statusIcon){switch(this.state.status){case'running':statusText.textContent='Läuft';statusIcon.className='fas fa-circle text-success';break;case'paused':statusText.textContent='Pausiert';statusIcon.className='fas fa-circle text-warning';break;case'expired':statusText.textContent='Abgelaufen';statusIcon.className='fas fa-circle text-danger';break;default:statusText.textContent='Gestoppt';statusIcon.className='fas fa-circle text-secondary';}}}
formatTime(seconds){const minutes=Math.floor(seconds/60);const secs=seconds%60;return`${minutes.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;}
async syncWithServer(){try{const response=await this.apiCall('status','GET');if(response.success&&response.data){const serverState=response.data;this.state.remaining=serverState.remaining_seconds||this.state.remaining;this.state.total=serverState.duration_seconds||this.state.total;this.state.status=serverState.status||this.state.status;this.updateDisplay();this.updateControls();this.updateStatusIndicator();this.state.lastServerSync=new Date();}}catch(error){console.error('Server-Synchronisation fehlgeschlagen:',error);}}
startServerSync(){if(this.intervals.serverSync){clearInterval(this.intervals.serverSync);}
this.intervals.serverSync=setInterval(()=>{this.syncWithServer();},30000);}
async apiCall(action,method='GET',data=null){const url=`${this.config.apiBase}/${this.config.name}/${action}`;const options={method,headers:{'Content-Type':'application/json','X-CSRFToken':this.getCSRFToken()}};if(data&&(method==='POST'||method==='PUT')){options.body=JSON.stringify(data);}
const response=await fetch(url,options);return await response.json();}
getCSRFToken(){const token=document.querySelector('meta[name="csrf-token"]');return token?token.getAttribute('content'):'';}
showNotification(title,message){if('Notification'in window&&Notification.permission==='granted'){new Notification(title,{body:message,icon:'/static/icons/timer-icon.png'});}}
showToast(message,type='info'){console.log(`Toast[${type}]:${message}`);}
showError(message){this.showModal('Fehler',message,'danger');}
showModal(title,message,type='info'){console.log(`Modal[${type}]${title}:${message}`);}
handleKeyboardShortcuts(e){if(e.ctrlKey||e.metaKey){switch(e.key){case' ':e.preventDefault();if(this.state.status==='running'){this.pause();}else{this.start();}
break;case'r':e.preventDefault();this.reset();break;case's':e.preventDefault();this.stop();break;}}}
handleVisibilityChange(){if(document.hidden){this.config._wasRunning=this.state.status==='running';}else{if(this.config.syncWithServer){this.syncWithServer();}}}
handleBeforeUnload(e){if(this.state.status==='running'&&this.state.remaining>0){e.preventDefault();e.returnValue='Timer läuft noch. Möchten Sie die Seite wirklich verlassen?';return e.returnValue;}}
destroy(){this.stopCountdown();if(this.intervals.serverSync){clearInterval(this.intervals.serverSync);}
if(this.elements.wrapper){this.elements.wrapper.remove();}
this.listeners.forEach((listener,element)=>{element.removeEventListener(listener.event,listener.handler);});console.log(`Timer'${this.config.name}'zerstört`);}}
const timerStyles=``;if(!document.getElementById('countdown-timer-styles')){document.head.insertAdjacentHTML('beforeend',timerStyles);}
window.CountdownTimer=CountdownTimer;window.TimerManager={timers:new Map(),create(name,options){if(this.timers.has(name)){console.warn(`Timer'${name}'existiert bereits`);return this.timers.get(name);}
const timer=new CountdownTimer({...options,name:name});this.timers.set(name,timer);return timer;},get(name){return this.timers.get(name);},destroy(name){const timer=this.timers.get(name);if(timer){timer.destroy();this.timers.delete(name);}},destroyAll(){this.timers.forEach(timer=>timer.destroy());this.timers.clear();}};