
// S.O.L.O. WorkManager Offline-Lizenzsystem v1.0.68
(function(){
  'use strict';

  const PUBLIC_KEY_PEM = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFgSSpQIaokYUxv/ZWkmA5WOE4jMx\n21+sXupBDiinsHFN15i8ixPIp7SLjkm/pGasXWP02Op5s5mErGLyL+oxgg==\n-----END PUBLIC KEY-----\n";
  const STORAGE_KEY = 'wm_solo_workmanager_license';
  const LIMITS = {
    demo: {
      workspaces: 1,
      projects: 3,
      tasks: 20,
      todos: 10,
      pspElements: 5,
      tags: 10,
      milestones: 10
    }
  };

  const de = {
    demo: 'Demo-Version',
    private: 'Privatlizenz',
    business: 'Gewerbelizenz',
    full: 'Vollversion',
    invalid: 'Ungültiger Lizenzschlüssel.',
    active: 'Lizenz aktiviert.',
    missingCrypto: 'Die Lizenzprüfung wird von diesem Browser nicht unterstützt.',
    limitReached: (kind, max) => `Demo-Grenze erreicht: maximal ${max} ${kind}. Bitte aktiviere eine Lizenz.`
  };
  const en = {
    demo: 'Demo version',
    private: 'Private license',
    business: 'Business license',
    full: 'Full version',
    invalid: 'Invalid license key.',
    active: 'License activated.',
    missingCrypto: 'This browser does not support license verification.',
    limitReached: (kind, max) => `Demo limit reached: maximum ${max} ${kind}. Please activate a license.`
  };

  function lang(){ return (localStorage.getItem('wm_language') || 'de') === 'en' ? 'en' : 'de'; }
  function txt(){ return lang()==='en' ? en : de; }
  function b64uToBytes(value){
    value = String(value || '').replace(/-/g, '+').replace(/_/g, '/');
    while(value.length % 4) value += '=';
    const bin = atob(value);
    const out = new Uint8Array(bin.length);
    for(let i=0;i<bin.length;i++) out[i] = bin.charCodeAt(i);
    return out;
  }
  function bytesToText(bytes){ return new TextDecoder().decode(bytes); }
  function pemToArrayBuffer(pem){
    const b64 = pem.replace(/-----BEGIN PUBLIC KEY-----/g,'').replace(/-----END PUBLIC KEY-----/g,'').replace(/\s+/g,'');
    return b64uToBytes(b64).buffer;
  }
  function safeJsonParse(value, fallback){ try { return JSON.parse(value); } catch(e){ return fallback; } }
  function nowIso(){ return new Date().toISOString(); }

  async function importPublicKey(){
    if(!window.crypto || !crypto.subtle) throw new Error('NO_WEBCRYPTO');
    return crypto.subtle.importKey(
      'spki',
      pemToArrayBuffer(PUBLIC_KEY_PEM),
      { name: 'ECDSA', namedCurve: 'P-256' },
      false,
      ['verify']
    );
  }

  async function verifyLicenseKey(key){
    const clean = String(key || '').trim().replace(/\s+/g, '');
    if(!clean.startsWith('PWK1-')) return { valid:false, reason:'FORMAT' };
    const parts = clean.slice(5).split('.');
    if(parts.length !== 2) return { valid:false, reason:'FORMAT' };
    const payloadBytes = b64uToBytes(parts[0]);
    const sigBytes = b64uToBytes(parts[1]);
    const publicKey = await importPublicKey();
    const ok = await crypto.subtle.verify({name:'ECDSA', hash:'SHA-256'}, publicKey, sigBytes, payloadBytes);
    if(!ok) return { valid:false, reason:'SIGNATURE' };
    const payload = safeJsonParse(bytesToText(payloadBytes), null);
    if(!payload || payload.product !== 'SOLO_WORKMANAGER') return { valid:false, reason:'PRODUCT' };
    if(!['private','business'].includes(payload.type)) return { valid:false, reason:'TYPE' };
    if(payload.expiresAt && new Date(payload.expiresAt).getTime() < Date.now()) return { valid:false, reason:'EXPIRED', payload };
    return { valid:true, payload, licenseKey: clean, verifiedAt: nowIso() };
  }

  function load(){
    const raw = localStorage.getItem(STORAGE_KEY);
    const rec = safeJsonParse(raw || '', null);
    if(rec && rec.valid && rec.payload && ['private','business'].includes(rec.payload.type)) return rec;
    return { valid:false, payload:{ type:'demo' } };
  }
  function save(rec){ localStorage.setItem(STORAGE_KEY, JSON.stringify(rec)); window.dispatchEvent(new Event('solo-workmanager-license-changed')); }
  function reset(){ localStorage.removeItem(STORAGE_KEY); if(window._pwLog) window._pwLog('INFO','Local license removed',{}); window.dispatchEvent(new Event('solo-workmanager-license-changed')); }
  function status(){ return load(); }
  function isDemo(){ return !load().valid; }
  function type(){ const s=load(); return s.valid ? s.payload.type : 'demo'; }
  function typeLabel(){ const t=type(); return t==='business' ? txt().business : (t==='private' ? txt().private : txt().demo); }
  function normalizeKind(kind){
    return ({workspaces:'Workspaces', projects:'Projekte', tasks:'Aufgaben', todos:'To-dos', pspElements:'PSP-Elemente', tags:'Tags', milestones:'Meilensteine'})[kind] || kind;
  }
  function isExampleWorkspace(item){
    if(!item) return false;
    const id = String(item.id || '');
    const name = String(item.name || '').toLowerCase();
    return item.isExampleWorkspace === true || id === '1780252722993_0uy0824' || name.indexOf('beispiel') >= 0 || name.indexOf('example workspace') >= 0;
  }
  function quotaItems(kind, currentItems){
    const list = Array.isArray(currentItems) ? currentItems : [];
    if(kind === 'workspaces') return list.filter(function(item){ return !isExampleWorkspace(item); });
    return list;
  }
  function canCreate(kind, currentItems, incoming){
    if(!isDemo()) return true;
    if(incoming && incoming.id) return true; // Bearbeiten bestehender Elemente bleibt erlaubt.
    const max = LIMITS.demo[kind];
    if(!max) return true;
    const count = quotaItems(kind, currentItems).length;
    if(count < max) return true;
    alert(txt().limitReached(normalizeKind(kind), max));
    return false;
  }

  async function activate(key){
    let result;
    try { result = await verifyLicenseKey(key); }
    catch(e){ return { ok:false, message: txt().missingCrypto, error:e }; }
    if(!result.valid) return { ok:false, message: txt().invalid, result };
    save(result);
    if(window._pwLog) window._pwLog('INFO','License activated',{type:result.payload&&result.payload.type, licenseId:result.payload&&result.payload.licenseId, owner:result.payload&&(result.payload.owner||result.payload.company||result.payload.email)});
    return { ok:true, message: txt().active, result };
  }

  function uiHtml(){
    const s = status();
    const p = s.payload || {};
    const isEn = lang()==='en';
    const title = isEn ? 'License' : 'Lizenz';
    const statusText = s.valid ? (isEn ? 'Activated' : 'Aktiviert') : (isEn ? 'Demo version' : 'Demo-Version');
    const typeText = typeLabel();
    const ownerText = p.owner || p.email || p.company || '—';
    const idText = p.licenseId || '—';
    const hint = isEn
      ? 'S.O.L.O. WorkManager starts as a demo and can be activated offline with a license key. The key can identify a private or business license.'
      : 'S.O.L.O. WorkManager startet als Demo und kann offline mit einem Lizenzschlüssel aktiviert werden. Der Schlüssel erkennt Privat- oder Gewerbelizenz.';
    const limits = isEn
      ? 'Demo limits: 1 own workspace, 3 projects, 20 tasks, 10 to-dos, 5 PSP elements, 10 tags, 10 milestones. The integrated example workspace does not count toward the workspace limit.'
      : 'Demo-Grenzen: 1 eigener Workspace, 3 Projekte, 20 Aufgaben, 10 To-dos, 5 PSP-Elemente, 10 Tags, 10 Meilensteine. Der integrierte Beispiel-Workspace zählt nicht zum Workspace-Limit.';
    return `
      <section class="license-card" data-solo-workmanager-license-card="1">
        <div class="license-card-head">
          <div>
            <div class="license-title">${title}</div>
            <div class="license-sub">${hint}</div>
          </div>
          <span class="license-badge ${s.valid ? 'ok':'demo'}">${typeText}</span>
        </div>
        <div class="license-grid">
          <div><span>${isEn?'Status':'Status'}</span><strong>${statusText}</strong></div>
          <div><span>${isEn?'License type':'Lizenztyp'}</span><strong>${typeText}</strong></div>
          <div><span>${isEn?'Licensed to':'Lizenziert für'}</span><strong>${ownerText}</strong></div>
          <div><span>${isEn?'License ID':'Lizenz-ID'}</span><strong>${idText}</strong></div>
        </div>
        ${s.valid ? '' : `<div class="license-limit-note">${limits}</div>`}
        <div class="license-input-row">
          <textarea class="form-input license-key-input" rows="3" placeholder="${isEn?'Paste license key here...':'Lizenzschlüssel hier einfügen...'}"></textarea>
          <button class="btn btn-primary license-activate-btn">${isEn?'Activate license':'Lizenz aktivieren'}</button>
        </div>
        <div class="license-actions">
          <button class="btn btn-ghost btn-sm license-reset-btn">${isEn?'Remove local license':'Lokale Lizenz entfernen'}</button>
          <span class="license-message"></span>
        </div>
      </section>`;
  }

  function findSettingsBody(){
    // v1.0.50: robust settings detection.
    // The backup-side-layout uses the original React settings page-body on the left
    // and an absolutely positioned backup panel on the right. Earlier versions tried
    // to locate the settings page only via its title; after layout changes this could
    // fail or append the license card outside the visible scroll column.
    var bodies = Array.from(document.querySelectorAll('.main-content .page-body'));
    if (!bodies.length) bodies = Array.from(document.querySelectorAll('.page-body'));

    function isVisible(el){
      return !!(el && el.offsetParent !== null && el.getBoundingClientRect().width > 0 && el.getBoundingClientRect().height > 0);
    }
    function hasSettingsText(el){
      var container = el && (el.parentElement || el);
      var text = container ? (container.textContent || '') : '';
      return /Einstellungen|Settings|Erscheinungsbild|Appearance|Arbeitszeiten|Working hours|Sprache \/ Language/.test(text);
    }

    var active = document.querySelector('.nav-item.active');
    var settingsActive = !!(active && /Einstellungen|Settings/.test(active.textContent || ''));
    if (settingsActive) {
      var match = bodies.find(function(b){ return isVisible(b) && hasSettingsText(b); }) || bodies.find(isVisible) || bodies[0];
      if (match) return match;
    }

    var titles = Array.from(document.querySelectorAll('.page-title,h1'))
      .filter(function(el){ return /Einstellungen|Settings/.test((el.textContent||'').trim()); });
    var title = titles.find(isVisible) || titles[0];
    if(title){
      var page = title.closest('div[style*="flex-direction"]') || title.closest('main') || document.body;
      return page.querySelector('.page-body') || page;
    }
    return null;
  }

  function injectSettings(force){
    var body = findSettingsBody();
    if(!body) return;

    // Remove duplicate cards that may have been inserted outside the settings body.
    Array.from(document.querySelectorAll('[data-solo-workmanager-license-card="1"]')).forEach(function(el){
      if(el.parentElement !== body) el.remove();
    });

    var card = body.querySelector('[data-solo-workmanager-license-card="1"]');
    if(card && !force) {
      // Keep the card as the last visible section in the left settings column.
      if (card !== body.lastElementChild) body.appendChild(card);
      return;
    }

    var wrap = document.createElement('div');
    wrap.innerHTML = uiHtml().trim();
    var fresh = wrap.firstChild;
    fresh.className = (fresh.className || '') + ' mt-3';
    fresh.style.display = 'block';
    fresh.style.width = '100%';
    fresh.style.boxSizing = 'border-box';

    if(card) card.replaceWith(fresh);
    else body.appendChild(fresh);
    card = fresh;

    var input = card.querySelector('.license-key-input');
    var msg = card.querySelector('.license-message');
    var activateBtn = card.querySelector('.license-activate-btn');
    var resetBtn = card.querySelector('.license-reset-btn');
    if(activateBtn) activateBtn.addEventListener('click', async function(){
      msg.textContent = lang()==='en' ? 'Checking...' : 'Prüfe...';
      var res = await activate(input.value);
      msg.textContent = res.message;
      if(res.ok) setTimeout(function(){ injectSettings(true); refreshBadges(); }, 250);
    });
    if(resetBtn) resetBtn.addEventListener('click', function(){
      if(confirm(lang()==='en' ? 'Remove the locally stored license?' : 'Lokal gespeicherte Lizenz entfernen?')){
        reset(); injectSettings(true); refreshBadges();
      }
    });
  }

  function refreshBadges(){
    const text = typeLabel();
    document.querySelectorAll('.solo-workmanager-license-top-badge').forEach(el => el.remove());
    const version = document.querySelector('.sidebar-bottom .mono');
    if(version){ version.textContent = 'v1.0.68 — lokal · ' + text; }
  }

  function startUiObserver(){
    const run = () => { refreshBadges(); }; // v1.0.50: Lizenzverwaltung hat eigenen System-Tab
    run();
    const obs = new MutationObserver(() => { clearTimeout(window.__pwLicUiTimer); window.__pwLicUiTimer = setTimeout(run, 80); });
    obs.observe(document.body, {childList:true, subtree:true});
    window.addEventListener('solo-workmanager-license-changed', run);
    window.addEventListener('storage', run);
  }

  window.SoloWorkManagerLicense = { verifyLicenseKey, activate, status, isDemo, type, typeLabel, canCreate, limits: LIMITS.demo, reset, refreshBadges };
  if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', startUiObserver);
  else startUiObserver();
})();
