const Pyrk = {
  base: window.PYRK_API_BASE || '../api',
  csrf: window.PYRK_CSRF || '',
  timeoutMs: Number(window.PYRK_API_TIMEOUT_MS || 12000),

  url(endpoint, params = {}) {
    const url = new URL(this.base.replace(/\/$/, '') + '/' + endpoint, window.location.href);
    Object.entries(params).forEach(([k, v]) => {
      if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, v);
    });
    return url.toString();
  },

  async parseResponse(response) {
    const text = await response.text();
    try {
      const json = text ? JSON.parse(text) : {};
      if (!response.ok && json && typeof json === 'object') {
        json.ok = false;
        json.message = json.message || `Request failed (${response.status})`;
      }
      return json;
    } catch (error) {
      const clean = String(text || '')
        .replace(/<script[\s\S]*?<\/script>/gi, ' ')
        .replace(/<style[\s\S]*?<\/style>/gi, ' ')
        .replace(/<[^>]*>/g, ' ')
        .replace(/\s+/g, ' ')
        .trim();
      return {
        ok: false,
        status: response.status,
        message: clean
          ? clean.slice(0, 1200)
          : `Request failed (${response.status}). Check PHP error log or browser Network tab.`,
        raw: text
      };
    }
  },

  async request(url, options = {}) {
    const controller = new AbortController();
    const timeoutMs = Number(options.timeoutMs || this.timeoutMs || 12000);
    const timeout = setTimeout(() => controller.abort(), timeoutMs);
    try {
      const response = await fetch(url, {...options, signal: controller.signal});
      return await this.parseResponse(response);
    } catch (error) {
      const message = error && error.name === 'AbortError'
        ? `Request timed out after ${Math.round(timeoutMs / 1000)} seconds. The popup was protected from hanging. API URL: ${url}`
        : (error && error.message ? error.message : 'Network request failed.');
      return {ok: false, message};
    } finally {
      clearTimeout(timeout);
    }
  },

  async get(endpoint, params = {}) {
    return await this.request(this.url(endpoint, params), {credentials: 'same-origin'});
  },

  async post(endpoint, data = {}) {
    return await this.request(this.url(endpoint), {
      method: 'POST',
      credentials: 'same-origin',
      headers: {'Content-Type': 'application/json', 'X-CSRF-Token': this.csrf},
      body: JSON.stringify(data)
    });
  },

  async postForm(endpoint, formData, options = {}) {
    return await this.request(this.url(endpoint), {
      method: 'POST',
      credentials: 'same-origin',
      headers: {'X-CSRF-Token': this.csrf},
      body: formData,
      timeoutMs: options.timeoutMs || undefined
    });
  },

  formatDate(value) {
    if (!value) return '—';
    const s = String(value).trim();
    const m = s.match(/^(\d{4})-(\d{2})-(\d{2})/);
    if (m) return `${m[3]}/${m[2]}/${m[1]}`;
    const dmy = s.match(/^(\d{2})\/(\d{2})\/(\d{4})/);
    if (dmy) return `${dmy[1]}/${dmy[2]}/${dmy[3]}`;
    return s;
  },

  formatDateTime(value) {
    if (!value) return '—';
    const s = String(value).trim().replace('T', ' ');
    const date = this.formatDate(s.slice(0, 10));
    const time = s.length >= 16 ? s.slice(11, 16) : '';
    return time ? `${date} ${time}` : date;
  },

  isoFromDmy(value) {
    const s = String(value || '').trim();
    const dmy = s.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
    if (dmy) return `${dmy[3]}-${dmy[2]}-${dmy[1]}`;
    return s;
  },

  dmyFromIso(value) {
    return this.formatDate(value);
  },

  esc(value) {
    return String(value ?? '').replace(/[&<>'"]/g, c => ({'&':'&amp;', '<':'&lt;', '>':'&gt;', "'":'&#39;', '"':'&quot;'}[c]));
  },

  toast(id, res) {
    const el = document.getElementById(id);
    if (!el) return;
    el.textContent = res.message || '';
    el.className = 'notice ' + (res.ok ? 'ok' : 'bad');
  },

  openModal(idOrElement) {
    const modal = typeof idOrElement === 'string' ? document.getElementById(idOrElement) : idOrElement;
    if (!modal) return;
    modal.hidden = false;
    modal.removeAttribute('hidden');
    modal.style.display = '';
    modal.setAttribute('aria-hidden', 'false');
    modal.classList.add('is-open');
    document.body.classList.add('modal-open');
  },

  closeModal(idOrElement) {
    let modal = typeof idOrElement === 'string' ? document.getElementById(idOrElement) : idOrElement;
    if (!modal) return;
    if (!modal.classList.contains('modal-backdrop')) {
      modal = modal.closest ? modal.closest('.modal-backdrop') : modal;
    }
    if (!modal) return;
    modal.hidden = true;
    modal.setAttribute('hidden', 'hidden');
    modal.style.display = 'none';
    modal.setAttribute('aria-hidden', 'true');
    modal.classList.remove('is-open');
    if (!document.querySelector('.modal-backdrop:not([hidden])')) {
      document.body.classList.remove('modal-open');
    }
  },

  closeAllModals() {
    document.querySelectorAll('.modal-backdrop').forEach(modal => this.closeModal(modal));
  },

  showModalError(containerId, title, message) {
    const el = document.getElementById(containerId);
    if (!el) return;
    el.innerHTML = `<div class="notice bad modal-error-box"><strong>${this.esc(title)}</strong><br>${this.esc(message)}<br><br><button class="btn btn-light btn-small" type="button" onclick="Pyrk.closeAllModals()">Close</button></div>`;
  }
};

// Global modal safety layer. Works for every page, even if page-specific JS breaks.
(function installPyrkModalSafety() {
  if (window.__PYRK_MODAL_SAFETY_INSTALLED__) return;
  window.__PYRK_MODAL_SAFETY_INSTALLED__ = true;

  document.addEventListener('click', event => {
    const closeButton = event.target.closest('[data-close-modal], [data-modal-close], .js-close-modal');
    if (closeButton) {
      event.preventDefault();
      event.stopPropagation();
      const modalId = closeButton.dataset.closeModal || closeButton.dataset.modalClose;
      if (modalId) Pyrk.closeModal(modalId);
      else Pyrk.closeModal(closeButton);
      return;
    }

    const opener = event.target.closest('[data-open-modal]');
    if (opener) {
      event.preventDefault();
      Pyrk.openModal(opener.dataset.openModal);
      return;
    }

    const backdrop = event.target.classList && event.target.classList.contains('modal-backdrop') ? event.target : null;
    if (backdrop) Pyrk.closeModal(backdrop);
  }, true);

  document.addEventListener('keydown', event => {
    if (event.key !== 'Escape') return;
    Pyrk.closeAllModals();
  });

  // Safety: any modal shipped with hidden must stay hidden after CSS loads.
  document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll('.modal-backdrop[hidden]').forEach(modal => {
      modal.style.display = 'none';
      modal.setAttribute('aria-hidden', 'true');
    });
  });
})();
