;(function(global){
  'use strict';
  function formatETA(seconds){
    if (!isFinite(seconds) || seconds < 0) return '—';
    const m = Math.floor(seconds/60);
    const s = Math.max(0, Math.round(seconds%60));
    return (m>0? m+':'+String(s).padStart(2,'0') : '0:'+String(s).padStart(2,'0'));
  }
  function clamp(n,min,max){ return Math.max(min, Math.min(max, n)); }

  function ProgressBar(container, opts){
    this.container = (typeof container==='string') ? document.querySelector(container) : container;
    if (!this.container) throw new Error('ProgressBar: container not found');
    this.opts = Object.assign({
      label: 'Progress',
      cancellable: true,
      stallThresholdMs: 10000
    }, opts||{});
    this.state = 'idle'; // idle|running|stalled|error|complete|cancelled
    this.percent = 0;
    this.startedAt = null;
    this.lastUpdateAt = null;
    this.etaSeconds = null;
    this._stallTimer = null;
    this._build();
  }
  ProgressBar.prototype._build = function(){
    this.wrap = document.createElement('div');
    this.wrap.className = 'progress-wrap';
    // Track & Fill
    this.track = document.createElement('div');
    this.track.className = 'progress-track';
    this.fill = document.createElement('div');
    this.fill.className = 'progress-fill';
    this.track.appendChild(this.fill);
    // Meta
    this.meta = document.createElement('div');
    this.meta.className = 'progress-meta';
    this.left = document.createElement('div');
    this.left.textContent = this.opts.label;
    this.right = document.createElement('div');
    // Actions
    this.actions = document.createElement('div');
    this.actions.className = 'progress-actions';
    this.cancelBtn = document.createElement('button');
    this.cancelBtn.type='button';
    this.cancelBtn.className='progress-cancel';
    this.cancelBtn.textContent='Cancel';
    this.cancelBtn.addEventListener('click', this.cancel.bind(this));
    if (this.opts.cancellable) this.actions.appendChild(this.cancelBtn);
    this.meta.appendChild(this.left);
    this.meta.appendChild(this.actions);
    // Live region
    this.live = document.createElement('div');
    this.live.setAttribute('aria-live','polite');
    this.live.className = 'sr-only';
    // Assemble
    this.wrap.appendChild(this.track);
    this.wrap.appendChild(this.meta);
    this.wrap.appendChild(this.live);
    this.container.innerHTML = '';
    this.container.appendChild(this.wrap);
    // ARIA
    this.track.setAttribute('role','progressbar');
    this.track.setAttribute('aria-valuemin','0');
    this.track.setAttribute('aria-valuemax','100');
    this.track.setAttribute('aria-label', this.opts.label);
    // Keyboard: Esc to cancel
    this.track.tabIndex = 0;
    this.track.addEventListener('keydown', (e)=>{
      if (e.key==='Escape' && this.opts.cancellable) { this.cancel(); }
    });
  };
  ProgressBar.prototype._setStallTimer = function(){
    clearTimeout(this._stallTimer);
    var self=this;
    this._stallTimer = setTimeout(function(){
      if (self.state==='running') {
        self.state='stalled';
        self.fill.classList.add('stalled'); self.fill.classList.remove('error','complete');
        self._renderMeta('Stalled at '+self.percent+'%');
      }
    }, this.opts.stallThresholdMs);
  };
  ProgressBar.prototype._renderMeta = function(text){
    // Right side shows % and ETA; left shows label; live region mirrors
    const eta = (this.etaSeconds!=null)? (' • ~'+formatETA(this.etaSeconds)+' remaining') : '';
    const pct = ' '+this.percent+'%';
    this.right.textContent = this.percent+'%'+eta+(text? ' • '+text : '');
    if (!this.meta.contains(this.right)) this.meta.insertBefore(this.right, this.actions);
    this.live.textContent = (this.opts.label+': '+this.right.textContent);
    this.track.setAttribute('aria-valuenow', String(this.percent));
  };
  ProgressBar.prototype.start = function(){
    this.state='running';
    this.percent=0;
    this.startedAt = Date.now();
    this.lastUpdateAt = Date.now();
    this.fill.style.width='0%';
    this.fill.classList.remove('error','complete','stalled');
    this._renderMeta();
    this._setStallTimer();
  };
  ProgressBar.prototype.update = function(percent, meta){
    if (this.state==='error' || this.state==='complete' || this.state==='cancelled') return;
    this.state='running';
    this.percent = clamp(Math.round(percent),0,100);
    this.fill.style.width = this.percent+'%';
    const now = Date.now();
    this.lastUpdateAt = now;
    // ETA estimation
    if (this.percent>0) {
      const elapsed = (now - this.startedAt)/1000;
      const rate = this.percent/elapsed; // percent per s
      if (rate>0) this.etaSeconds = ((100 - this.percent)/rate);
    }
    this._renderMeta(meta);
    this.fill.classList.remove('error','complete','stalled');
    if (this.percent>=100) { this.complete(); return; }
    this._setStallTimer();
  };
  ProgressBar.prototype.error = function(message){
    this.state='error';
    this.fill.classList.add('error'); this.fill.classList.remove('complete','stalled');
    this._renderMeta('Error: '+(message||'failed'));
    clearTimeout(this._stallTimer);
  };
  ProgressBar.prototype.complete = function(){
    this.state='complete';
    this.percent=100; this.fill.style.width='100%';
    this.fill.classList.add('complete'); this.fill.classList.remove('error','stalled');
    this.etaSeconds=0; this._renderMeta('Completed');
    clearTimeout(this._stallTimer);
  };
  ProgressBar.prototype.cancel = function(){
    if (!this.opts.cancellable || this.state==='complete') return;
    this.state='cancelled';
    this._renderMeta('Cancelled');
    clearTimeout(this._stallTimer);
    if (typeof this.opts.onCancel==='function') this.opts.onCancel();
  };
  ProgressBar.prototype.destroy = function(){
    clearTimeout(this._stallTimer);
    if (this.wrap && this.container.contains(this.wrap)) this.container.removeChild(this.wrap);
  };

  global.GoLegalProgressBar = ProgressBar;
})(window);
