<?php
// =============================
// Voice Isolator — Button & Queue Always Visible (Direct XHR)
// • Get Quote button is ALWAYS visible (never disabled by default)
// • Visible file queue even before selection; additive + de‑dupe + remove
// • No upload or redirect until you click Get Quote
// • Direct XHR to your n8n webhook (matches original flow)
// • After quote, redirect to Stripe via url or session_id
// • After Stripe return (?session_id=...), poll exchange webhook for download
// =============================

// ---------- Config ----------
$N8N_HOST   = "https://n8n.1522irondome.synology.me"; // your n8n host
$QUOTE_PATH = "/webhook/e9e3e103-d339-41da-815b-baca6cc9513a"; // quote webhook (real)
$EXCHG_PATH = "/webhook/1f185e38-db32-41b3-9532-5d0e732bde42"; // exchange webhook (real)

// ---------- Security headers ----------
$nonce = base64_encode(random_bytes(16));
header("X-Content-Type-Options: nosniff");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("X-Frame-Options: DENY");
header("Permissions-Policy: interest-cohort=()");
// Allow direct calls to n8n (XHR) and Stripe Checkout iframes. (This is CSP, not CORS.)
header("Content-Security-Policy: default-src 'self'; img-src 'self' data: https://*.stripe.com https://podcastpontifications.com; style-src 'self' 'unsafe-inline'; script-src 'self' https://*.stripe.com 'nonce-$nonce'; connect-src 'self' $N8N_HOST; frame-src https://*.stripe.com; frame-ancestors 'none'; base-uri 'none'");

$sessionId = $_GET['session_id'] ?? '';
$canceled  = $_GET['canceled']  ?? '';
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Voice Isolator</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <meta name="color-scheme" content="dark light">
  <style>
    :root { --bg:#0b1022; --card:#111827; --muted:#9aa4b2; --text:#e5e7eb; --accent:#60a5fa; --ok:#34d399; --err:#f87171; --border:rgba(148,163,184,.25); color-scheme: dark; }
    html,body { height:100% }
    body { margin:0; background:linear-gradient(180deg,#0b1022,#0e132b); color:var(--text); font:14px/1.45 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial; }
    .wrap { max-width:980px; margin:40px auto; padding:0 16px }
    .logo-container { text-align: center; margin-bottom: 20px; }
    .card { background:rgba(17,24,39,.9); border:1px solid var(--border); border-radius:18px; padding:26px; box-shadow:0 14px 36px rgba(0,0,0,.35) }
    .header { display:flex; gap:14px; align-items:center; }
    .logo-img { width:240px; height:auto; border-radius:8px; }
    h1 { margin:0; font-size:22px }
    p.sub { margin:.25rem 0 1rem; color:var(--muted) }

    .grid { display:grid; grid-template-columns: 1fr 320px; gap:22px; }
    @media (max-width:880px){ .grid{ grid-template-columns:1fr; } }

    .dropzone { border:1px dashed var(--border); border-radius:14px; padding:18px; background:#0f172a; display:flex; gap:12px; align-items:center; justify-content:center; text-align:center; min-height:120px; cursor:pointer; position:relative }
    .dropzone:hover { border-color:#7aa2ff55 }
    .dz-icon { width:36px; height:36px; border-radius:10px; background:#111827; display:grid; place-items:center; }
    .dz-text { color:#cbd5e1 }
    .file-input-overlay{ position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer; }

    .row { display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-top:12px }
    input[type=file], input[type=email] { padding:12px; border-radius:12px; border:1px solid var(--border); background:#0f172a; color:#d1d5db; }
    input[type=file]{ flex:1 }

    .btn { padding:12px 16px; border-radius:12px; border:1px solid var(--border); background:linear-gradient(180deg,#1f2937,#111827); color:#e5e7eb; cursor:pointer; font-weight:600; }
    .btn.primary { border-color:#3b82f6; background:linear-gradient(180deg,#2563eb,#1d4ed8) }

    .status { margin-top:10px; padding:10px; border-radius:12px; border:1px solid var(--border); background:rgba(59,130,246,.08); color:#c7d2fe; min-height:20px }
    .status.err { background:rgba(248,113,113,.1); color:#fecaca; border-color:#f8717140; }
    .status.ok  { background:rgba(52,211,153,.1); color:#bbf7d0; border-color:#34d39933; }

    .filelist { margin:10px 0 0; padding:0; list-style:none; border:1px dashed var(--border); border-radius:12px; background:#0b1228; }
    .filelist.empty::before { content:'No files selected yet'; display:block; padding:10px; color:var(--muted); }
    .filelist li { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:8px 10px; border-top:1px solid var(--border); }
    .filelist li:first-child { border-top:none }
    .remove-btn { background:transparent; border:none; color: var(--muted); cursor:pointer; font-size: 18px; line-height: 1; padding: 2px 4px; }
    .remove-btn:hover { color: var(--err); }

    .progress { appearance:none; width:100%; height:10px; border-radius:999px; background:#0f172a; border:1px solid var(--border); overflow:hidden; margin-top:10px }
    .progress::-webkit-progress-value{ background:linear-gradient(90deg,#2563eb,#60a5fa) }
    .progress::-moz-progress-bar{ background:linear-gradient(90deg,#2563eb,#60a5fa) }
  </style>
</head>
<body>
  <div class="wrap">
    <div class="card">
        <div class="logo-container">
  <img src="https://podcastpontifications.com/wp-content/themes/podcastpontifications.com/images/mylogo.png" alt="Logo" class="logo-img" />
</div>
      <div class="header">
        <div>
          <h1>Voice Isolator</h1>
          <p class="sub">Add files, then click <em>Get Quote</em>.</p> 
          <p class="sub">We have small overhead costs to offer this service so any job will result in a $.99 minimum charge and is otherwise based on audio file length. Longer files cost more money, but we worked hard to price so we can cover costs. Please note this is a new service, and it might take several minutes for the process to complete. We are actively working to make things faster!</p>
          <p class="sub">DO NOT Resbumit Jobs Over and Over If It Isn't Working - Any issues please email info@podcastpontifications.com</p>
        </div>
      </div>

      <div class="grid">
        <div>
          <form id="quoteForm" autocomplete="on" novalidate>
            <div id="dropzone" class="dropzone" tabindex="0">
              <div class="dz-icon">📁</div>
              <div class="dz-text">
                <strong>Drag & drop audio files</strong> or click to select
                <div class="muted" style="margin-top:4px">WAV, MP3, M4A, AAC, FLAC, OGG, AIFF, CAF…</div>
              </div>
              <input id="files" name="files" type="file" multiple class="file-input-overlay"
                     accept="audio/*,.wav,.mp3,.m4a,.aac,.flac,.ogg,.webm,.aiff,.aif,.caf" autocomplete="off"
                     aria-label="Choose audio files" title="Choose audio files" />
            </div>

            <!-- File queue is ALWAYS visible (starts empty) -->
            <ul id="fileList" class="filelist empty"></ul>

             <!-- Email submission temp disabled -->
            <div class="row">
               <?php /*<input id="email" name="email" type="email" placeholder="Email (optional for receipt)" style="flex:1"
                     autocomplete="email" inputmode="email" autocapitalize="off" spellcheck="false"*/ ?>
              <button id="btnQuote" class="btn primary" type="button">Get Quote</button>
            </div>

            <progress id="progress" class="progress" max="100" value="0"></progress>
            <div id="status" class="status">Waiting for files…</div>

            <?php /*<details id="debugPanel" style="margin-top:10px">
              <summary class="muted">Debug</summary>
              <pre id="resp" style="max-height:300px; overflow:auto; background:#0b1228; border:1px solid var(--border); border-radius:12px; padding:10px">(none)</pre>
            </details>*/ ?>
          </form>
        </div>

        <aside class="aside" style="border:1px solid var(--border); border-radius:14px; padding:14px; background:#0f172a">
          <h3 style="margin:.25rem 0 .5rem; font-size:14px">How it works</h3>
          <p>1) Add your audio file(s).</p>
          <p>2) Click <em>Get Quote</em> — we upload to our service and bring you to the Price + Checkout.</p>
          <p>3) We redirect you to Stripe Checkout.</p>
          <p>4) After payment you’ll return here; we fetch your download link automatically. It might take several minutes for the file to process. Files are delivered as .zip.</p>
          <p>5) Note: We are actively working to improve the speed of the service for longer files, especially if sending multiple long files. Please reach out to support if you have an issue: info@podcastpontifications.com.</p> 
        </aside>
      </div>

      <?php if ($canceled): ?>
      <div class="status err" style="margin-top:12px">Payment canceled. You can re‑run a quote above any time.</div>
      <?php endif; ?>

      <?php if ($sessionId): ?>
      <div id="successPane" style="margin-top:14px">
        <div id="status2" class="status"><span id="statusText2">Confirming payment and preparing your download…</span></div>
        <div class="row" style="margin-top:10px">
          <button id="btnDownload" class="btn primary" disabled>Preparing…</button>
          <span id="expires" class="muted"></span>
        </div>
        <details style="margin-top:10px"><summary class="muted">Show technical details</summary><pre id="resp2" style="max-height:320px; overflow:auto; background:#0b1228; border:1px solid var(--border); border-radius:12px; padding:10px">(waiting…)</pre></details>
      </div>
      <?php endif; ?>

    </div>
  </div>

  <script nonce="<?php echo $nonce; ?>">
  (function(){
    // --- Endpoints (direct to n8n) ---
    const WEBHOOK_URL   = "<?php echo $N8N_HOST . $QUOTE_PATH; ?>";
    const EXCHANGE_URL  = "<?php echo $N8N_HOST . $EXCHG_PATH; ?>";
    const sessionId     = "<?php echo htmlspecialchars($sessionId, ENT_QUOTES); ?>";

    const $ = (id) => document.getElementById(id);
    const dz = $('dropzone');
    const fileInput = $('files');
    const fileList = $('fileList');
    const btnQuote = $('btnQuote');
    const statusEl = $('status');
    const progressEl = $('progress');
    const respEl = $('resp');

    const setStatus = (m, cls) => { statusEl.className = 'status' + (cls ? ' ' + cls : ''); statusEl.textContent = m; };
    const setProgress = (n) => { progressEl.value = Math.max(0, Math.min(100, n|0)); };

    // ---- Additive queue (de‑dupe) ----
    let selectedFiles = [];
    const key = (f) => [f.name, f.size, f.lastModified].join('|');
    function addFiles(list){ const map = new Map(selectedFiles.map(f=>[key(f), f])); Array.from(list||[]).forEach(f=>map.set(key(f), f)); selectedFiles = Array.from(map.values()); renderList(); if (fileInput) fileInput.value = ''; }
    function removeAt(i){ selectedFiles.splice(i,1); renderList(); }
    function renderList(){ fileList.innerHTML=''; if(!selectedFiles.length){ fileList.classList.add('empty'); setStatus('Waiting for files…'); return; } fileList.classList.remove('empty'); selectedFiles.forEach((f,i)=>{ const li=document.createElement('li'); const left=document.createElement('span'); left.textContent=f.name; const right=document.createElement('span'); right.className='muted'; right.textContent=(f.size/1e6).toFixed(2)+' MB'; const rm=document.createElement('button'); rm.type='button'; rm.className='remove-btn'; rm.innerHTML='\u00d7'; rm.title='Remove'; rm.onclick=()=>removeAt(i); li.append(left,right,rm); fileList.appendChild(li); }); setStatus(selectedFiles.length+' file(s) ready.','ok'); }

    dz.addEventListener('dragover', (e)=>{ e.preventDefault(); dz.style.borderColor = '#7aa2ff88'; });
    dz.addEventListener('dragleave', ()=>{ dz.style.borderColor = 'var(--border)'; });
    dz.addEventListener('drop', (e)=>{ e.preventDefault(); dz.style.borderColor = 'var(--border)'; if(e.dataTransfer?.files?.length){ addFiles(e.dataTransfer.files); } });
    fileInput.addEventListener('change', (e)=> addFiles(e.target.files||[]));

    function showDebug(label, xhr, parsed, raw){ const out = { label, httpStatus: xhr?.status, url: xhr?.responseURL, headers: xhr ? xhr.getAllResponseHeaders() : null, json: parsed, rawText: raw }; if (respEl) respEl.textContent = JSON.stringify(out, null, 2); }

    // ---- Get Quote → XHR to n8n → redirect ----
    btnQuote.addEventListener('click', ()=>{
      const email = (document.getElementById('email')||{}).value?.trim?.() || '';
      if (!selectedFiles.length){ setStatus('Please add at least one audio file.', 'err'); return; }
      setStatus('Uploading and requesting quote…'); setProgress(0);

      const form = new FormData();
      // Use the same naming as your original working hosted script: 'file', 'file_1', ...
      selectedFiles.forEach((f,i)=> form.append(i===0 ? 'file' : `file_${i}`, f, f.name));
      if (email) form.append('email', email);
      // If your flow expects it: form.append('checkout_mode','hosted');

      const xhr = new XMLHttpRequest();
      xhr.open('POST', WEBHOOK_URL, true);
      xhr.responseType = 'text';
      xhr.setRequestHeader('Accept', 'application/json');

      xhr.upload.onprogress = (e)=>{ if (e.lengthComputable){ const pct = Math.round((e.loaded/e.total)*100); setProgress(pct); setStatus(`Uploading… ${pct}%`); } };

      xhr.onload = ()=>{
        const ct = (xhr.getResponseHeader('Content-Type')||'').toLowerCase();
        const txt = xhr.response || '';
        let data = null; try{ data = JSON.parse(txt); }catch{}
        showDebug('[quote:xhr]', xhr, data, ct.includes('html') ? '(HTML page)' : txt);

        if (xhr.status >= 200 && xhr.status < 300){
          const d = Array.isArray(data) ? data[0] : data || {};
          const url = d.checkout_url || d.url || d.redirect_url || null;
          const sid = d.session_id || d.sessionId || d.id || null;
          if (url){ setStatus('Redirecting to Stripe…','ok'); location.href = url; return; }
          if (sid){ setStatus('Redirecting to Stripe…','ok'); location.href = `https://checkout.stripe.com/c/pay/${sid}`; return; }
          setStatus('Quote ready but missing checkout URL. See details below.','err');
        } else {
          setStatus(`Server error (${xhr.status}). See details below.`, 'err');
        }
      };

      xhr.onerror = ()=>{ setStatus('Network error. If you see CORS, enable CORS on n8n webhook.', 'err'); };
      xhr.send(form);
    });

    // ---- Success mode (after Stripe) ----
    if (sessionId){
      const status2 = document.getElementById('status2');
      const resp2 = document.getElementById('resp2');
      const btnDownload = document.getElementById('btnDownload');
      function setStatus2(m, cls){ if(!status2) return; status2.className = 'status' + (cls ? ' ' + cls : ''); status2.firstChild.textContent = m; }

      function exchangeOnce(){ return new Promise((resolve)=>{
        const xhr = new XMLHttpRequest();
        xhr.open('POST', EXCHANGE_URL, true);
        xhr.responseType = 'text';
        xhr.setRequestHeader('Accept','application/json');
        xhr.setRequestHeader('Content-Type','application/json');
        xhr.onload = ()=>{ const txt = xhr.response||''; let j=null; try{ j=JSON.parse(txt);}catch{}; if(resp2) resp2.textContent = JSON.stringify({ httpStatus:xhr.status, json:j, rawText:txt }, null, 2); resolve({xhr,j}); };
        xhr.onerror = ()=> resolve({xhr:null,j:null});
        xhr.send(JSON.stringify({ session_id: sessionId }));
      }); }

      (async()=>{
        setStatus2('Finalizing your download…');
        const sleep=(ms)=>new Promise(r=>setTimeout(r,ms));
        let attempt=0, final=null; const MAX=10; const BASE=1200, JIT=400;
        while(attempt++<MAX){ const {xhr,j}=await exchangeOnce(); const url=j?.download_url||j?.url||j?.file||j?.file_url||''; const done=j?.status==='done'||j?.state==='succeeded'; if(((xhr&&xhr.status>=200&&xhr.status<300)&&url) || (done&&url)){ final=j; break; } const retry=j?.retry||j?.status==='processing'||j?.status==='pending'||(!xhr||xhr.status>=500); if(!retry) break; await sleep(Math.max(250, BASE*attempt + Math.floor((Math.random()*2-1)*JIT))); }
        if(!final){ setStatus2('We couldn’t retrieve the download link yet. Please try again shortly.','err'); return; }
        const raw = final.download_url || final.url || '';
        const exp = final.expires_at || final.expiresAt || '';
        const fn  = final.filename || '';
        if (raw && btnDownload){ btnDownload.disabled=false; btnDownload.onclick=()=>{ try{ const a=document.createElement('a'); a.href=raw; a.rel='noopener'; a.click(); }catch(e){ location.href=raw; } }; if(fn) btnDownload.textContent = 'Download ' + fn; setStatus2('Ready. Click to download your cleaned audio.','ok'); }
        else { setStatus2('Processing complete but no download URL was returned.','err'); }
        if (exp){ const ex = document.getElementById('expires'); if (ex) ex.textContent = 'Expires: ' + exp; }
      })();
    }
  })();
  </script>
</body>
</html>
