<!--
  APPLY RATE WIDGET
  Paste this block ONCE near the end of <body> in BOTH reservation_form.php and
  reservation_list.php (before </body>). It provides:
    - a Bootstrap 5 modal for picking a rate plan and scope
    - a global window.openApplyRate(orderId, opts) function to launch it

  Requirements:
    - Bootstrap 5 JS (already loaded in RoomZ)
    - apply_rate.php reachable at the path in APPLY_RATE_URL below (adjust if needed)

  Usage:
    From the reservation LIST, on a row button:
      <button type="button" class="btn btn-sm btn-outline-primary"
              onclick="openApplyRate(123)">Apply Rate</button>

    From the reservation FORM (editing order 123):
      <button type="button" class="btn btn-outline-primary"
              onclick="openApplyRate(123, { onDone: function(){ location.reload(); } })">
        Apply Rate
      </button>
-->

<div class="modal fade" id="applyRateModal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-lg modal-dialog-scrollable">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title"><i class="bi bi-cash-coin me-2"></i>Apply Rate</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">

        <div id="applyRateAlert" class="alert d-none" role="alert"></div>

        <div class="mb-3">
          <label class="form-label fw-semibold">Rate Plan</label>
          <select id="applyRatePlan" class="form-select">
            <option value="">Loading rate plans…</option>
          </select>
          <div class="form-text">Choose the contract / rate plan to apply to this booking.</div>
        </div>

        <div class="mb-3">
          <label class="form-label fw-semibold d-block">Apply to</label>
          <div class="btn-group" role="group">
            <input type="radio" class="btn-check" name="applyRateScope" id="applyRateScopeAll" value="all" checked>
            <label class="btn btn-outline-secondary" for="applyRateScopeAll">Entire stay</label>

            <input type="radio" class="btn-check" name="applyRateScope" id="applyRateScopeSel" value="selected">
            <label class="btn btn-outline-secondary" for="applyRateScopeSel">Selected days</label>
          </div>
        </div>

        <div id="applyRateDaysWrap" class="mb-3 d-none">
          <label class="form-label fw-semibold d-block">Select nights</label>
          <div id="applyRateDays" class="d-flex flex-wrap gap-2"></div>
          <div class="form-text">Tick the nights to update. Unticked nights keep their current rate.</div>
        </div>

        <div class="mb-3">
          <div class="form-check">
            <input class="form-check-input" type="checkbox" id="applyRateFlatToggle">
            <label class="form-check-label" for="applyRateFlatToggle">
              Enter a flat nightly total instead of recomputing from the plan
            </label>
          </div>
          <div id="applyRateFlatWrap" class="mt-2 d-none">
            <div class="input-group" style="max-width: 260px;">
              <span class="input-group-text">Per night</span>
              <input type="number" step="0.01" min="0" id="applyRateFlatValue" class="form-control" placeholder="0.00">
            </div>
            <div class="form-text">This exact total is applied to each night in scope and flagged as a manual override.</div>
          </div>
        </div>

        <div class="mb-3">
          <label class="form-label">Reason / note (optional)</label>
          <input type="text" id="applyRateReason" class="form-control" placeholder="e.g. Contracted rate correction">
        </div>

        <div id="applyRatePreview" class="d-none">
          <hr>
          <h6 class="fw-semibold">Preview</h6>
          <div class="table-responsive">
            <table class="table table-sm align-middle mb-2">
              <thead><tr><th>Date</th><th>Plan</th><th class="text-end">Night total</th></tr></thead>
              <tbody id="applyRatePreviewBody"></tbody>
              <tfoot>
                <tr class="fw-semibold">
                  <td colspan="2" class="text-end">New total for selected nights</td>
                  <td class="text-end" id="applyRatePreviewTotal">0.00</td>
                </tr>
              </tfoot>
            </table>
          </div>

          <div id="applyRateFolioDelta" class="card border-0 bg-light mt-2 d-none">
            <div class="card-body py-2">
              <h6 class="fw-semibold mb-2"><i class="bi bi-journal-check me-1"></i>Master folio impact</h6>
              <div id="applyRateFolioNew" class="alert alert-warning py-1 px-2 small mb-2 d-none">
                <i class="bi bi-plus-circle me-1"></i>No master folio exists yet — one will be created.
              </div>
              <table class="table table-sm mb-0">
                <thead>
                  <tr class="small text-muted">
                    <th></th><th class="text-end">Before</th><th class="text-end">After</th><th class="text-end">Change</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <td>Room charges</td>
                    <td class="text-end" id="fbRoomBefore">0.00</td>
                    <td class="text-end" id="fbRoomAfter">0.00</td>
                    <td class="text-end" id="fbRoomDelta">0.00</td>
                  </tr>
                  <tr>
                    <td>Other charges</td>
                    <td class="text-end" id="fbOtherBefore">0.00</td>
                    <td class="text-end" id="fbOtherAfter">0.00</td>
                    <td class="text-end text-muted">—</td>
                  </tr>
                  <tr>
                    <td>Payments</td>
                    <td class="text-end" id="fbPayBefore">0.00</td>
                    <td class="text-end" id="fbPayAfter">0.00</td>
                    <td class="text-end text-muted">—</td>
                  </tr>
                  <tr class="fw-semibold border-top">
                    <td>Balance due</td>
                    <td class="text-end" id="fbBalBefore">0.00</td>
                    <td class="text-end" id="fbBalAfter">0.00</td>
                    <td class="text-end" id="fbBalDelta">0.00</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
        </div>

      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-outline-primary" id="applyRatePreviewBtn">
          <i class="bi bi-eye me-1"></i>Preview
        </button>
        <button type="button" class="btn btn-primary" id="applyRateApplyBtn" disabled>
          <i class="bi bi-check2-circle me-1"></i>Apply Rate
        </button>
      </div>
    </div>
  </div>
</div>

<script>
(function () {
  // Adjust this if apply_rate.php lives elsewhere relative to the page.
  // Absolute web-root path so the fetch works regardless of how the host page
  // URL is structured (rewrites, trailing slashes, different module folders).
  const APPLY_RATE_URL = '/rooms/modules/reservations/apply_rate.php';

  let arState = { orderId: 0, onDone: null, plansLoaded: false };

  function arEl(id) { return document.getElementById(id); }

  function arAlert(msg, kind) {
    const box = arEl('applyRateAlert');
    if (!box) return;
    box.className = 'alert alert-' + (kind || 'info');
    box.textContent = msg;
    box.classList.remove('d-none');
  }
  function arClearAlert() {
    const box = arEl('applyRateAlert');
    if (box) box.classList.add('d-none');
  }

  function arScope() {
    const sel = document.querySelector('input[name="applyRateScope"]:checked');
    return sel ? sel.value : 'all';
  }

  function arSelectedDates() {
    return Array.from(document.querySelectorAll('.apply-rate-day:checked'))
      .map(function (c) { return c.value; });
  }

  function arFlatValue() {
    if (!arEl('applyRateFlatToggle').checked) return '';
    const v = arEl('applyRateFlatValue').value;
    return (v === '' || isNaN(parseFloat(v))) ? '' : parseFloat(v).toFixed(2);
  }

  async function arPost(action, extra) {
    const body = new FormData();
    body.append('ar_action', action);
    body.append('order_id', arState.orderId);
    if (extra) Object.keys(extra).forEach(function (k) { body.append(k, extra[k]); });
    const res = await fetch(APPLY_RATE_URL, { method: 'POST', body: body, credentials: 'same-origin' });
    const text = await res.text();
    try { return JSON.parse(text); }
    catch (e) { return { ok: false, error: 'Bad server response: ' + text.slice(0, 200) }; }
  }

  async function arLoadPlans() {
    const sel = arEl('applyRatePlan');
    if (!sel) { console.error('[ApplyRate] plan <select> not found in DOM'); return; }
    try {
      const data = await arPost('list_plans');
      if (!data.ok) { arAlert(data.error || 'Failed to load rate plans.', 'danger'); return; }
      sel.innerHTML = '<option value="">— Select a rate plan —</option>';
      (data.plans || []).forEach(function (p) {
        const meal = p.meal_plan ? ' (' + p.meal_plan + ')' : '';
        const inactive = String(p.status) !== '1' ? ' [inactive]' : '';
        const opt = document.createElement('option');
        opt.value = p.id;
        opt.textContent = p.title + meal + inactive;
        sel.appendChild(opt);
      });
      arState.plansLoaded = true;
    } catch (err) {
      console.error('[ApplyRate] load plans failed:', err);
      arAlert('Could not load rate plans: ' + (err && err.message ? err.message : err), 'danger');
    }
  }

  async function arLoadDays() {
    // Fetch the nights via a preview with a dummy: instead we ask preview with
    // scope=all once a plan is chosen. But to list nights before a plan is
    // picked, we call a lightweight preview that returns existing dates.
    // Simpler: derive nights from the first successful preview. Here we just
    // reveal the container; days get populated on first preview.
  }

  function arRenderDays(dates) {
    const wrap = arEl('applyRateDays');
    wrap.innerHTML = '';
    dates.forEach(function (d) {
      const id = 'ard_' + d;
      const div = document.createElement('div');
      div.className = 'form-check form-check-inline';
      div.innerHTML =
        '<input class="form-check-input apply-rate-day" type="checkbox" value="' + d + '" id="' + id + '" checked>' +
        '<label class="form-check-label" for="' + id + '">' + d + '</label>';
      wrap.appendChild(div);
    });
  }

  function arMoney(n) { return Number(n || 0).toFixed(2); }

  function arRenderFolioDelta(before, after) {
    const wrap = arEl('applyRateFolioDelta');
    if (!wrap || !before || !after) return;
    wrap.classList.remove('d-none');

    arEl('applyRateFolioNew').classList.toggle('d-none', !!before.folio_exists);

    arEl('fbRoomBefore').textContent = arMoney(before.room_charges);
    arEl('fbRoomAfter').textContent  = arMoney(after.room_charges);
    const roomDelta = Number(after.room_charges) - Number(before.room_charges);
    const rd = arEl('fbRoomDelta');
    rd.textContent = (roomDelta >= 0 ? '+' : '') + arMoney(roomDelta);
    rd.className = 'text-end ' + (roomDelta > 0 ? 'text-danger' : (roomDelta < 0 ? 'text-success' : 'text-muted'));

    arEl('fbOtherBefore').textContent = arMoney(before.other_charges);
    arEl('fbOtherAfter').textContent  = arMoney(after.other_charges);
    arEl('fbPayBefore').textContent   = arMoney(before.payments);
    arEl('fbPayAfter').textContent    = arMoney(after.payments);

    arEl('fbBalBefore').textContent = arMoney(before.balance);
    arEl('fbBalAfter').textContent  = arMoney(after.balance);
    const balDelta = Number(after.balance) - Number(before.balance);
    const bd = arEl('fbBalDelta');
    bd.textContent = (balDelta >= 0 ? '+' : '') + arMoney(balDelta);
    bd.className = 'text-end fw-semibold ' + (balDelta > 0 ? 'text-danger' : (balDelta < 0 ? 'text-success' : 'text-muted'));
  }

  function arRenderPreview(data) {
    const body = arEl('applyRatePreviewBody');
    body.innerHTML = '';
    (data.changes || []).forEach(function (c) {
      const tr = document.createElement('tr');
      tr.innerHTML =
        '<td>' + c.date + '</td>' +
        '<td>' + (c.rate_plan_title || '') + '</td>' +
        '<td class="text-end">' + Number(c.night_total).toFixed(2) + '</td>';
      body.appendChild(tr);
    });
    arEl('applyRatePreviewTotal').textContent = Number(data.new_total || 0).toFixed(2);
    arEl('applyRatePreview').classList.remove('d-none');
    arRenderFolioDelta(data.folio_before, data.folio_after);
    // Populate day chips from the preview if in 'selected' mode and empty.
    if (arScope() === 'selected' && !document.querySelector('.apply-rate-day')) {
      arRenderDays((data.changes || []).map(function (c) { return c.date; }));
    }
  }

  function arValidate() {
    const planId = arEl('applyRatePlan').value;
    if (!planId) { arAlert('Choose a rate plan first.', 'warning'); return null; }
    const extra = { rate_plan_id: planId, scope: arScope() };
    if (arScope() === 'selected') {
      const days = arSelectedDates();
      if (!days.length) { arAlert('Tick at least one night, or switch to Entire stay.', 'warning'); return null; }
      extra.dates = JSON.stringify(days);
    }
    const flat = arFlatValue();
    if (arEl('applyRateFlatToggle').checked && flat === '') {
      arAlert('Enter a valid flat nightly total, or untick the flat option.', 'warning'); return null;
    }
    if (flat !== '') extra.flat_total = flat;
    const reason = arEl('applyRateReason').value.trim();
    if (reason) extra.reason = reason;
    return extra;
  }

  async function arDoPreview() {
    arClearAlert();
    const extra = arValidate();
    if (!extra) return;
    arEl('applyRatePreviewBtn').disabled = true;
    const data = await arPost('preview', extra);
    arEl('applyRatePreviewBtn').disabled = false;
    if (!data.ok) { arAlert(data.error || 'Preview failed.', 'danger'); arEl('applyRateApplyBtn').disabled = true; return; }
    arRenderPreview(data);
    if (data.missing && data.missing.length) {
      arAlert('No plan price for: ' + data.missing.join(', ') + '. These nights will be skipped.', 'warning');
    }
    arEl('applyRateApplyBtn').disabled = false;
  }

  async function arDoApply() {
    arClearAlert();
    const extra = arValidate();
    if (!extra) return;

    // Require a preview first so staff always see the folio impact.
    if (arEl('applyRatePreview').classList.contains('d-none')) {
      arAlert('Please Preview first to review the folio impact before applying.', 'info');
      return;
    }

    const balAfter = arEl('fbBalAfter').textContent;
    const balDelta = arEl('fbBalDelta').textContent;
    if (!confirm('Apply this rate to the selected nights?\n\nNew balance due: ' + balAfter +
                 ' (change ' + balDelta + ')\n\nThe master folio will be updated.')) return;

    arEl('applyRateApplyBtn').disabled = true;
    const data = await arPost('apply', extra);
    if (!data.ok) { arAlert(data.error || 'Apply failed.', 'danger'); arEl('applyRateApplyBtn').disabled = false; return; }

    let msg = data.message || 'Rate applied.';
    if (data.folio_after) {
      msg += ' New folio balance: ' + Number(data.folio_after.balance).toFixed(2) + '.';
    }
    arAlert(msg, 'success');

    // Close the Apply Rate modal immediately after a successful apply.
    const modalEl = arEl('applyRateModal');
    if (modalEl && typeof bootstrap !== 'undefined' && bootstrap.Modal) {
      const modal = bootstrap.Modal.getInstance(modalEl);
      if (modal) modal.hide();
    }

    if (typeof arState.onDone === 'function') {
      // Allow Bootstrap to finish removing the backdrop before refreshing.
      setTimeout(function () { arState.onDone(data); }, 250);
    }
  }

  // When switching to "selected", we need the night list. Trigger a preview
  // with scope=all first (if a plan is chosen) to harvest dates, else prompt.
  async function arOnScopeChange() {
    const isSel = arScope() === 'selected';
    arEl('applyRateDaysWrap').classList.toggle('d-none', !isSel);
    if (isSel && !document.querySelector('.apply-rate-day')) {
      const planId = arEl('applyRatePlan').value;
      if (planId) {
        const data = await arPost('preview', { rate_plan_id: planId, scope: 'all' });
        if (data.ok) arRenderDays((data.changes || []).map(function (c) { return c.date; }));
      } else {
        arAlert('Pick a rate plan to load the nights.', 'info');
      }
    }
  }

  window.openApplyRate = function (orderId, opts) {
    opts = opts || {};
    console.log('[ApplyRate] openApplyRate called with orderId=', orderId);
    arState.orderId = parseInt(orderId, 10) || 0;
    arState.onDone = opts.onDone || null;
    if (!arState.orderId) { alert('No booking id — save the booking first.'); return; }

    const modalEl = arEl('applyRateModal');
    if (!modalEl) { alert('Apply Rate dialog not found on page. The widget may not be included correctly.'); console.error('[ApplyRate] #applyRateModal missing'); return; }
    if (typeof bootstrap === 'undefined' || !bootstrap.Modal) {
      alert('Bootstrap modal library not loaded — cannot open the dialog.');
      console.error('[ApplyRate] bootstrap.Modal unavailable');
      return;
    }

    // reset UI
    arClearAlert();
    arEl('applyRatePreview').classList.add('d-none');
    arEl('applyRateFolioDelta').classList.add('d-none');
    arEl('applyRatePreviewBody').innerHTML = '';
    arEl('applyRateApplyBtn').disabled = true;
    arEl('applyRateDays').innerHTML = '';
    arEl('applyRateDaysWrap').classList.add('d-none');
    arEl('applyRateScopeAll').checked = true;
    arEl('applyRateFlatToggle').checked = false;
    arEl('applyRateFlatWrap').classList.add('d-none');
    arEl('applyRateFlatValue').value = '';
    arEl('applyRateReason').value = '';

    if (!arState.plansLoaded) arLoadPlans();

    // Ensure the modal is a direct child of <body> so a transformed/filtered
    // ancestor can't trap its position:fixed (a common "backdrop but no modal"
    // cause). Safe to run every open.
    if (modalEl.parentNode !== document.body) {
      document.body.appendChild(modalEl);
    }

    const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
    modal.show();
    // Belt-and-braces: after Bootstrap inserts the backdrop, guarantee the
    // dialog paints above it and any sticky page chrome.
    setTimeout(function () {
      modalEl.style.zIndex = '20000';
      var bd = document.querySelector('.modal-backdrop:last-of-type');
      if (bd) bd.style.zIndex = '19990';
    }, 10);
  };
  console.log('[ApplyRate] widget script loaded, openApplyRate defined:', typeof window.openApplyRate);

  // Wire events reliably whether this widget is parsed before or after
  // DOMContentLoaded. Event delegation also survives the modal being moved
  // directly under <body> by openApplyRate().
  let arEventsBound = false;
  function arBindEvents() {
    if (arEventsBound) return;
    arEventsBound = true;

    const previewBtn = arEl('applyRatePreviewBtn');
    const applyBtn = arEl('applyRateApplyBtn');
    if (previewBtn) previewBtn.addEventListener('click', arDoPreview);
    if (applyBtn) applyBtn.addEventListener('click', arDoApply);

    document.addEventListener('change', async function (event) {
      const target = event.target;
      if (!target) return;

      if (target.matches('input[name="applyRateScope"]')) {
        await arOnScopeChange();
        return;
      }

      if (target.id === 'applyRateFlatToggle') {
        arEl('applyRateFlatWrap').classList.toggle('d-none', !target.checked);
        // A flat amount changes the calculation; require a fresh preview.
        arEl('applyRatePreview').classList.add('d-none');
        arEl('applyRateApplyBtn').disabled = true;
        return;
      }

      if (target.id === 'applyRatePlan') {
        arClearAlert();
        arEl('applyRatePreview').classList.add('d-none');
        arEl('applyRateApplyBtn').disabled = true;
        arEl('applyRateDays').innerHTML = '';

        if (!target.value) return;

        // Run Preview immediately after a plan is selected.
        // Under selected-day scope, first obtain the date checkboxes.
        if (arScope() === 'selected') {
          await arOnScopeChange();
        }
        await arDoPreview();
      }
    });
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', arBindEvents, { once: true });
  } else {
    arBindEvents();
  }
})();
</script>
