// Ollie Cosmos — license verification gate (Ollie Ecosystem standard).
// See _support/documentation/VERIFICATION_GUIDE.md.
//   app_code: ollie-cosmos · keys: cosmos-license-verified / cosmos-verification
//   ollie-complete master key + Desktop auto-activate are supported, so on Ollie
//   System this app activates silently (no modal).

const COSMOS_VERSION = '2026.6.48';

const VerificationSystem = {
  isVerified: false,
  pmUrl: null,
  modal: null,
  publicKey: null,

  PUBLIC_KEY_PEM: `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0DBFObvzLYJ10ArmvzOq
he+1BY9MU1GMKnz4JRYgTN5oBE/pO2OCijxppH38bdpA5iV3nFZvSsV2HBMw9o/q
rM2TvXCyO6kOvlqxeCvQ8gZbaH+p43KDgCRihPH3+R456LimvE5XhZK+Vhj4BDTZ
iijis9bfCamKPzXwzTWzszoAQlgVwGvKrxBxzn3apqh8NWe5hwHn+vC20euJ3/QZ
umHi/PCfZX5XJnsHBTOsM1dSkowRSOr+84R8X3g930kUiGtfQkHcGjrG6/+RsQWC
TjcujNTpLgZdDc4ldIxsaJKlP37SSdNLKjjH1KCk6eprNSmllSHudXgpWuFC1yf6
PQIDAQAB
-----END PUBLIC KEY-----`,

  async getPmUrl() {
    if (this.pmUrl) return this.pmUrl;
    try {
      const res = await fetch('/api/system/pm-url');
      const data = await res.json();
      this.pmUrl = data.url || 'https://pm.costa-and-associates.com';
    } catch (e) {
      this.pmUrl = 'https://pm.costa-and-associates.com';
    }
    return this.pmUrl;
  },

  async getCryptoKey() {
    if (this.publicKey) return this.publicKey;
    const header = '-----BEGIN PUBLIC KEY-----';
    const footer = '-----END PUBLIC KEY-----';
    const contents = this.PUBLIC_KEY_PEM.substring(header.length, this.PUBLIC_KEY_PEM.length - footer.length).replace(/\s/g, '');
    const bin = window.atob(contents);
    const der = new Uint8Array(bin.length);
    for (let i = 0; i < bin.length; i++) der[i] = bin.charCodeAt(i);
    this.publicKey = await window.crypto.subtle.importKey('spki', der, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['verify']);
    return this.publicKey;
  },

  async verifySignature(data, signature) {
    try {
      const key = await this.getCryptoKey();
      const sig = new Uint8Array(window.atob(signature).split('').map((c) => c.charCodeAt(0)));
      const sorted = Object.keys(data).sort().reduce((acc, k) => { acc[k] = data[k]; return acc; }, {});
      const bytes = new TextEncoder().encode(JSON.stringify(sorted));
      return await window.crypto.subtle.verify('RSASSA-PKCS1-v1_5', key, sig, bytes);
    } catch (e) { return false; }
  },

  async checkVerification() {
    let verified = localStorage.getItem('cosmos-license-verified') === 'true';
    if (verified) {
      const vData = localStorage.getItem('cosmos-verification');
      if (vData) {
        try {
          const parsed = JSON.parse(vData);
          const signature = parsed.signature;
          const storedAppCode = parsed.app_code || 'ollie-cosmos';
          if (!signature) {
            // Legacy unsigned receipt: de-activate ONLY on explicit revoke.
            if (await this.silentVerify(parsed.email, parsed.key, storedAppCode) === 'revoked') verified = false;
          } else {
            delete parsed.signature;
            if (!await this.verifySignature(parsed, signature)) {
              verified = false;
            } else if (parsed.expires_at && new Date() > new Date(parsed.expires_at)) {
              // Expiry is signed → tamper-proof and enforced offline.
              // Lifetime licenses have no expires_at and never hit this.
              verified = false;
            } else {
              // Best-effort revocation heartbeat: refresh when online, but
              // NEVER de-activate on a network failure — only on 'revoked'.
              const diffDays = (Date.now() - new Date(parsed.verified_at || 0)) / 86400000;
              if (diffDays > 7 && await this.silentVerify(parsed.email, parsed.key, storedAppCode) === 'revoked') verified = false;
            }
          }
        } catch (e) { verified = false; }
      }
    }
    this.isVerified = verified;
    return verified;
  },

  async silentVerify(email, key, appCode) {
    appCode = appCode || 'ollie-cosmos';
    try {
      const res = await fetch(`${await this.getPmUrl()}/api/verify`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ key, email, app_code: appCode }),
      });
      const data = await res.json();
      if (data.valid && data.signature) {
        localStorage.setItem('cosmos-verification', JSON.stringify(data));
        return 'valid';
      }
      if (data.valid === false) return 'revoked'; // explicit server-side revocation
    } catch (e) {}
    return 'unreachable'; // network/DNS/server down or ambiguous — stay activated
  },

  async tryDesktopAutoActivate() {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 2000);
      const res = await fetch('http://localhost:19000/api/desktop/complete-license', { signal: controller.signal });
      clearTimeout(timeout);
      if (!res.ok) return false;
      const { available, email, key } = await res.json();
      if (!available || !email || !key) return false;
      if (await this.silentVerify(email, key, 'ollie-complete') === 'valid') {
        localStorage.setItem('cosmos-license-verified', 'true');
        this.isVerified = true;
        if (window.SovereignStorage && window.SovereignStorage.sync) await window.SovereignStorage.sync();
        return true;
      }
    } catch (e) { /* desktop not running or timed out */ }
    return false;
  },

  showModal() {
    const appContainer = document.querySelector('.app-container');
    if (appContainer) appContainer.style.display = 'none';
    if (this.modal) { this.modal.style.display = 'flex'; return; }

    const modal = document.createElement('div');
    modal.id = 'verification-modal';
    modal.className = 'verification-modal-overlay';
    modal.innerHTML = `
      <div class="verification-modal">
        <div class="verification-modal-header">
          <h2>Welcome to Cosmos</h2>
          <p>Enter your email and license key to enter the world</p>
        </div>
        <div class="verification-modal-body">
          <form id="verification-form">
            <div class="verification-field">
              <label for="modal-verification-email">Email</label>
              <input type="email" id="modal-verification-email" placeholder="your@email.com" required />
            </div>
            <div class="verification-field">
              <label for="modal-verification-key">License Key</label>
              <input type="text" id="modal-verification-key" placeholder="XXXX-XXXX-XXXX-XXXX" required style="font-family: monospace;" />
            </div>
            <div id="verification-error" style="display:none;color:#f48771;margin-bottom:16px;font-size:14px;background:rgba(244,135,113,0.1);padding:12px;border-radius:6px;border:1px solid rgba(244,135,113,0.2);"></div>
            <button id="verification-submit" class="verification-btn primary">Activate License</button>
          </form>
        </div>
      </div>`;
    document.body.appendChild(modal);
    this.modal = modal;

    if (!document.getElementById('verification-modal-styles')) {
      const style = document.createElement('style');
      style.id = 'verification-modal-styles';
      style.textContent = `
        .verification-modal-overlay{position:fixed;inset:0;background:rgba(8,10,18,0.85);display:flex;align-items:center;justify-content:center;z-index:10000;backdrop-filter:blur(6px);}
        .verification-modal{background:#161b22;border-radius:14px;padding:2rem;max-width:460px;width:90%;box-shadow:0 12px 40px rgba(0,0,0,0.6);border:1px solid #30363d;color:#e6edf3;}
        .verification-modal-header{text-align:center;margin-bottom:1.6rem;}
        .verification-modal-header h2{margin:0 0 0.4rem;}
        .verification-modal-header p{margin:0;color:#9da7b3;font-size:0.9rem;}
        .verification-field{margin-bottom:1rem;}
        .verification-field label{display:block;margin-bottom:0.45rem;font-weight:500;font-size:0.9rem;color:#9da7b3;}
        .verification-field input{width:100%;padding:0.7rem;border:1px solid #30363d;border-radius:8px;background:#0d1117;color:#e6edf3;box-sizing:border-box;}
        .verification-btn{width:100%;padding:0.75rem;border:none;border-radius:8px;font-size:1rem;font-weight:600;cursor:pointer;background:#238636;color:#fff;margin-top:0.4rem;}
        .verification-btn:hover{opacity:0.92;} .verification-btn:disabled{opacity:0.6;cursor:not-allowed;}`;
      document.head.appendChild(style);
    }

    modal.querySelector('#verification-form').addEventListener('submit', async (e) => { e.preventDefault(); await this.handleVerify(); });
  },

  hideModal() {
    if (this.modal) this.modal.style.display = 'none';
  },

  async handleVerify() {
    const email = (document.getElementById('modal-verification-email') || {}).value?.trim().toLowerCase();
    const key = (document.getElementById('modal-verification-key') || {}).value?.trim();
    const errorDiv = document.getElementById('verification-error');
    const submitBtn = document.getElementById('verification-submit');
    submitBtn.disabled = true;
    submitBtn.textContent = 'Verifying...';
    errorDiv.style.display = 'none';

    try {
      const res = await fetch(`${await this.getPmUrl()}/api/verify`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ app_code: 'ollie-cosmos', key, email }),
      });
      let data;
      try { data = await res.json(); } catch (e) { throw new Error('Invalid response from server.'); }

      if (!data.valid) {
        // ollie-complete master key fallback
        try {
          const cr = await fetch(`${await this.getPmUrl()}/api/verify`, {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ app_code: 'ollie-complete', key, email }),
          });
          const cd = await cr.json();
          if (cd.valid && cd.signature) {
            data = cd;
            fetch('http://localhost:19000/api/desktop/activate-complete', {
              method: 'POST', headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ licenseData: cd }),
            }).catch(() => {});
          }
        } catch (e) { /* desktop may not be running */ }
      }

      if (data.valid && data.signature) {
        localStorage.setItem('cosmos-verification', JSON.stringify(data));
        localStorage.setItem('cosmos-license-verified', 'true');
        this.isVerified = true;
        if (window.SovereignStorage && window.SovereignStorage.sync) await window.SovereignStorage.sync();
        this.hideModal();
        initializeApp();
      } else {
        errorDiv.textContent = data.error || 'Invalid license key or email';
        errorDiv.style.display = 'block';
        submitBtn.disabled = false;
        submitBtn.textContent = 'Activate License';
      }
    } catch (err) {
      errorDiv.textContent = err.message || 'Network error. Please try again.';
      errorDiv.style.display = 'block';
      submitBtn.disabled = false;
      submitBtn.textContent = 'Activate License';
    }
  },
};

// Boot the world once verified: reveal the app container and start the Three.js
// viewport (exposed by viewport.js as window.__bootCosmos).
function initializeApp() {
  const c = document.querySelector('.app-container');
  if (c) c.style.display = '';
  if (window.__bootCosmos) window.__bootCosmos();
}

document.addEventListener('DOMContentLoaded', async () => {
  const c = document.querySelector('.app-container');
  if (c) c.style.display = 'none';
  if (window.SovereignStorage && window.SovereignStorage.ready) await window.SovereignStorage.ready;
  if (await VerificationSystem.checkVerification()) initializeApp();
  else if (await VerificationSystem.tryDesktopAutoActivate()) initializeApp();
  else VerificationSystem.showModal();
});

window.VerificationSystem = VerificationSystem;
window.checkVerificationStatus = async () => {
  if (!(await VerificationSystem.checkVerification())) {
    const c = document.querySelector('.app-container');
    if (c) c.style.display = 'none';
    VerificationSystem.showModal();
  }
};
window.showVerificationModal = () => VerificationSystem.showModal();
