'use strict';

const API = '';   // relative — works from any IP (localhost, Tailscale, LAN)
const SESSION_KEY = 'ams_session';

// ── Estado de sesión ────────────────────────────────────────────────────────
const Auth = {
  session: null,

  load() {
    try {
      const raw = localStorage.getItem(SESSION_KEY);
      this.session = raw ? JSON.parse(raw) : null;
    } catch { this.session = null; }
    this._renderBadge();
  },

  save(data) {
    this.session = data;
    localStorage.setItem(SESSION_KEY, JSON.stringify(data));
    this._renderBadge();
  },

  clear() {
    this.session = null;
    localStorage.removeItem(SESSION_KEY);
    this._renderBadge();
  },

  isLoggedIn()  { return !!this.session?.token; },
  isAdmin()     { return ['ADMIN','SUPERADMIN'].includes(this.session?.role); },
  isSuperAdmin(){ return this.session?.role === 'SUPERADMIN'; },
  token()       { return this.session?.token || ''; },

  _renderBadge() {
    const el = document.getElementById('session-badge');
    if (!el) return;
    if (!this.session) {
      el.innerHTML = `<span style="opacity:.5">NOT LOGGED IN</span>`;
      el.title = 'Click to login';
      el.onclick = () => Modal.openLogin(null);
    } else {
      const roleClass = {
        SUPERADMIN: 'role-superadmin',
        ADMIN:      'role-admin',
        USER:       'role-user',
      }[this.session.role] || 'role-user';
      el.innerHTML = `
        <span>${this.session.nombre}</span>
        <span class="session-role ${roleClass}">${this.session.role}</span>
      `;
      el.title = 'Click to logout';
      el.onclick = () => {
        if (confirm(`Logout as ${this.session.nombre}?`)) {
          Auth.clear();
          window._clearPortConstraints?.();
          _portCache = null;
          // Return to login screen
          document.getElementById('app').classList.add('hidden');
          document.getElementById('login-screen').classList.remove('hidden');
          document.getElementById('ls-user').value = '';
          document.getElementById('ls-pass').value = '';
          document.getElementById('ls-error').classList.add('hidden');
          setTimeout(() => document.getElementById('ls-user').focus(), 100);
        }
      };
    }
  },
};

// ── Modal controller ────────────────────────────────────────────────────────
const Modal = {
  _pendingAid: null,

  openLogin(aidData) {
    this._pendingAid = aidData;
    document.getElementById('login-user').value = '';
    document.getElementById('login-pass').value = '';
    document.getElementById('login-error').classList.add('hidden');
    _showOverlay('modal-login');
    setTimeout(() => document.getElementById('login-user').focus(), 100);
  },

  closeLogin() {
    _hideOverlay();
    this._pendingAid = null;
  },

  async submitLogin() {
    const username = document.getElementById('login-user').value.trim();
    const password = document.getElementById('login-pass').value;
    const errEl    = document.getElementById('login-error');
    const btn      = document.getElementById('btn-login-submit');

    if (!username || !password) return;
    btn.disabled = true;
    btn.textContent = 'AUTHENTICATING...';
    errEl.classList.add('hidden');

    try {
      const form = new URLSearchParams({ username, password });
      const res  = await fetch(`${API}/auth/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: form,
      });

      if (!res.ok) throw new Error('invalid');
      const data = await res.json();

      Auth.save({ token: data.access_token, username: data.username,
                  nombre: data.nombre, role: data.role });
      _hideOverlay();
      afterLogin();
      _fetchAndApplyPort(data.access_token, data.role);   // update constraints if user changed

      if (this._pendingAid) {
        this.openEdit(this._pendingAid);
        this._pendingAid = null;
      }
    } catch {
      errEl.textContent = 'Invalid credentials or insufficient permissions.';
      errEl.classList.remove('hidden');
    } finally {
      btn.disabled = false;
      btn.textContent = 'LOGIN';
    }
  },

  async openEdit(aidData) {
    // Si no tiene sesión con permisos → abrir login primero
    if (!Auth.isAdmin()) {
      this.openLogin(aidData);
      return;
    }

    document.getElementById('edit-aid-name').textContent = aidData.nombre;
    document.getElementById('edit-save-status').textContent = '';
    document.getElementById('edit-save-status').className = 'save-status';

    const body = document.getElementById('modal-edit-body');
    body.innerHTML = '<div style="padding:20px;color:var(--text-muted)">Loading…</div>';
    _showOverlay('modal-edit');

    // Refresh caches so the dropdowns show up-to-date contacts and lamps
    try {
      await Promise.all([
        window.loadContacts ? window.loadContacts() : Promise.resolve(),
        window.loadLamps    ? window.loadLamps()    : Promise.resolve(),
      ]);
    } catch {}

    body.innerHTML = buildEditForm(aidData);
    document.getElementById('btn-edit-submit').onclick = () => this.submitEdit(aidData);
  },

  closeEdit() { _hideOverlay(); },

  async submitEdit(aidData) {
    const btn    = document.getElementById('btn-edit-submit');
    const status = document.getElementById('edit-save-status');
    const motivo = document.getElementById('ef-motivo')?.value?.trim();

    if (!motivo) {
      status.textContent = 'Reason for change is required.';
      status.className   = 'save-status err';
      return;
    }

    btn.disabled = true;
    btn.textContent = 'SAVING...';
    status.textContent = '';

    // MMSI: empty string → unassign (sends "" so backend nulls the column).
    // Non-empty → must be digits-only; backend enforces uniqueness across aids.
    const mmsiVal = v('ef-mmsi');
    if (mmsiVal && !/^\d{6,9}$/.test(mmsiVal)) {
      status.textContent = 'MMSI must be 6–9 digits, or blank to unassign.';
      status.className   = 'save-status err';
      btn.disabled = false; btn.textContent = 'SAVE CHANGES';
      return;
    }

    const payload = {
      puerto_responsable:  v('ef-puerto')  || null,
      empresa_responsable: v('ef-empresa') || null,
      caracteristica_luz:  v('ef-luz')     || null,
      alcance_nm:          flt('ef-alcance'),
      radio_borneo_m:      flt('ef-borneo'),
      displacement_warn_m:  flt('ef-drift-warn'),
      displacement_alarm_m: flt('ef-drift-alarm'),
      signal_loss_min:      flt('ef-signal-loss') ? parseInt(v('ef-signal-loss')) : null,
      din3_function:        v('ef-din3') || null,
      din4_function:        v('ef-din4') || null,
      observaciones:        v('ef-obs')     || null,
      lamp_id:              v('ef-lamp')    || null,
      mmsi:                 mmsiVal,         // "" unassigns; backend handles
      tipo_ais:             mmsiVal ? 'ATON_21' : 'SIN_AIS',
      modificado_por:      Auth.session.nombre,
      motivo_cambio:       motivo,
    };

    // Posición nominal solo si superadmin y se modificó
    if (Auth.isSuperAdmin()) {
      const lat = flt('ef-lat');
      const lon = flt('ef-lon');
      if (lat) payload.lat_nominal = lat;
      if (lon) payload.lon_nominal = lon;
    }

    try {
      const res = await fetch(`${API}/aids/${aidData.id}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${Auth.token()}`,
        },
        body: JSON.stringify(payload),
      });

      if (res.status === 401) {
        Auth.clear();
        _hideOverlay();
        this.openLogin(aidData);
        return;
      }
      if (!res.ok) throw new Error(await res.text());

      const updated = await res.json();

      // Actualizar feature en el mapa
      const feature = window.aidsSource?.getFeatureById(aidData.id);
      if (feature) feature.setProperties({ ...feature.getProperties(), ...updated });

      // Refrescar panel info
      if (window.refreshInfoPanel) window.refreshInfoPanel({ ...aidData, ...updated });

      status.textContent = 'Saved successfully.';
      status.className   = 'save-status ok';
      setTimeout(() => _hideOverlay(), 1200);
    } catch (e) {
      status.textContent = 'Error: ' + e.message;
      status.className   = 'save-status err';
    } finally {
      btn.disabled = false;
      btn.textContent = 'SAVE CHANGES';
    }
  },
};

// ── Form builder ─────────────────────────────────────────────────────────────
function buildEditForm(p) {
  // Port options from PORT_AUTHORITY contacts (deduplicated by port_name)
  const contacts = window._contactsCache || [];
  const portsFromContacts = [...new Set(contacts
    .filter(c => c.role === 'PORT_AUTHORITY' && c.port_name)
    .map(c => c.port_name))];
  // Keep the current value as an option even if no contact uses it yet
  if (p.puerto_responsable && !portsFromContacts.includes(p.puerto_responsable)) {
    portsFromContacts.push(p.puerto_responsable);
  }
  const pOpts = portsFromContacts.map(x =>
    `<option value="${x}" ${p.puerto_responsable === x ? 'selected':''}>${x}</option>`).join('');

  // Company options from OWNER contacts (deduplicated by company_name)
  const companiesFromContacts = [...new Set(contacts
    .filter(c => c.role === 'OWNER' && c.company_name)
    .map(c => c.company_name))];
  if (p.empresa_responsable && !companiesFromContacts.includes(p.empresa_responsable)) {
    companiesFromContacts.push(p.empresa_responsable);
  }
  const cOpts = companiesFromContacts.map(x =>
    `<option value="${x}" ${p.empresa_responsable === x ? 'selected':''}>${x}</option>`).join('');

  // Lamp options from the lamp catalog
  const lamps = window._lampCache || [];
  const lOpts = lamps.map(l =>
    `<option value="${l.id}" ${l.id === p.lamp_id ? 'selected':''}>${l.manufacturer} ${l.model} (${l.voltage_min}–${l.voltage_max} V)</option>`).join('');

  const nominalBlock = Auth.isSuperAdmin() ? `
    <div class="modal-section-label">NOMINAL POSITION — SUPERADMIN ONLY</div>
    <div class="field-row-modal">
      <div class="form-field">
        <label class="form-label">Latitude</label>
        <input class="form-input" id="ef-lat" type="number" step="0.00001" value="${p.lat_nominal || ''}">
      </div>
      <div class="form-field">
        <label class="form-label">Longitude</label>
        <input class="form-input" id="ef-lon" type="number" step="0.00001" value="${p.lon_nominal || ''}">
      </div>
    </div>` : '';

  // AIS link — when set, AIS Type 21 with this MMSI updates the buoy's
  // lat_actual (ghost marker) and triggers drift/battery alerts using this
  // aid's per-buoy thresholds.
  const aisBlock = `
    <div class="modal-section-label">AIS LINK</div>
    <div style="font-size:0.68rem;color:var(--text-muted);margin-bottom:6px">
      MMSI of the AtoN transponder on this buoy.
      Once set, the system listens for AIS Type 21 with this MMSI and shows
      the AIS-reported position as a transparent ghost over the nominal marker.
      Leave blank if the buoy has no AIS.
    </div>
    <div class="form-field">
      <label class="form-label">MMSI</label>
      <input class="form-input" id="ef-mmsi" type="text" inputmode="numeric"
             pattern="\\d{6,9}" maxlength="9"
             value="${p.mmsi || ''}" placeholder="e.g. 993001002">
    </div>`;

  return `
    <div class="modal-section-label">GENERAL</div>

    <div class="form-field">
      <label class="form-label">Port Responsible
        <span style="font-size:0.6rem;color:var(--text-muted)">(from contacts catalog)</span>
      </label>
      <select class="form-input-select" id="ef-puerto">
        <option value="">-- Select --</option>
        ${pOpts}
      </select>
    </div>

    <div class="form-field">
      <label class="form-label">Company / Owner
        <span style="font-size:0.6rem;color:var(--text-muted)">(from contacts catalog)</span>
      </label>
      <select class="form-input-select" id="ef-empresa">
        <option value="">-- Select --</option>
        ${cOpts}
      </select>
    </div>

    <div class="form-field">
      <label class="form-label">Lamp Model
        <span style="font-size:0.6rem;color:var(--text-muted)">(from lamp catalog — sets battery thresholds)</span>
      </label>
      <select class="form-input-select" id="ef-lamp">
        <option value="">-- None --</option>
        ${lOpts}
      </select>
    </div>

    <div class="modal-section-label">TECHNICAL</div>

    <div class="field-row-modal">
      <div class="form-field">
        <label class="form-label">Light Characteristic</label>
        <input class="form-input" id="ef-luz" type="text"
               value="${p.caracteristica_luz || ''}" placeholder="e.g. Fl(2) R 10s">
      </div>
      <div class="form-field">
        <label class="form-label">Range (nm)</label>
        <input class="form-input" id="ef-alcance" type="number" step="0.5"
               value="${p.alcance_nm || ''}">
      </div>
    </div>

    <div class="form-field">
      <label class="form-label">Swing Radius (m)</label>
      <input class="form-input" id="ef-borneo" type="number" step="1"
             value="${p.radio_borneo_m ?? 10}">
    </div>

    <div class="modal-section-label">ALERT THRESHOLDS</div>
    <div style="font-size:0.68rem;color:var(--text-muted);margin-bottom:6px">
      Leave blank to use global settings. Override per buoy based on anchor chain length and operating area.
    </div>
    <div class="field-row-modal">
      <div class="form-field">
        <label class="form-label">Drift warn (m)</label>
        <input class="form-input" id="ef-drift-warn" type="number" step="1" min="1"
               value="${p.displacement_warn_m ?? ''}" placeholder="global">
      </div>
      <div class="form-field">
        <label class="form-label">Drift alarm (m)</label>
        <input class="form-input" id="ef-drift-alarm" type="number" step="1" min="1"
               value="${p.displacement_alarm_m ?? ''}" placeholder="global">
      </div>
      <div class="form-field">
        <label class="form-label">Signal loss (min)</label>
        <input class="form-input" id="ef-signal-loss" type="number" step="1" min="1"
               value="${p.signal_loss_min ?? ''}" placeholder="off">
      </div>
    </div>
    <div class="field-row-modal">
      <div class="form-field">
        <label class="form-label">Digital IN3 function</label>
        <select class="form-input-select" id="ef-din3">
          <option value="">— Not connected —</option>
          <option value="WATER_INGRESS_WARN"     ${p.din3_function==='WATER_INGRESS_WARN'     ?'selected':''}>Water ingress (warning)</option>
          <option value="WATER_INGRESS_CRITICAL" ${p.din3_function==='WATER_INGRESS_CRITICAL' ?'selected':''}>Water ingress (critical)</option>
          <option value="LISTING"                ${p.din3_function==='LISTING'                ?'selected':''}>Listing / tilt sensor</option>
        </select>
      </div>
      <div class="form-field">
        <label class="form-label">Digital IN4 function</label>
        <select class="form-input-select" id="ef-din4">
          <option value="">— Not connected —</option>
          <option value="WATER_INGRESS_WARN"     ${p.din4_function==='WATER_INGRESS_WARN'     ?'selected':''}>Water ingress (warning)</option>
          <option value="WATER_INGRESS_CRITICAL" ${p.din4_function==='WATER_INGRESS_CRITICAL' ?'selected':''}>Water ingress (critical)</option>
          <option value="LISTING"                ${p.din4_function==='LISTING'                ?'selected':''}>Listing / tilt sensor</option>
        </select>
      </div>
    </div>

    <div class="form-field">
      <label class="form-label">Observations</label>
      <textarea class="form-textarea" id="ef-obs"
                style="height:64px">${p.observaciones || ''}</textarea>
    </div>

    ${aisBlock}

    ${nominalBlock}

    <div class="modal-section-label">AUDIT</div>

    <div class="form-field">
      <label class="form-label">Modified by</label>
      <input class="form-input" value="${Auth.session?.nombre || ''}" disabled>
    </div>

    <div class="form-field">
      <label class="form-label">Reason for change <span style="color:var(--red)">*</span></label>
      <textarea class="form-textarea" id="ef-motivo"
                style="height:56px" placeholder="Mandatory — describe the change..."></textarea>
    </div>
  `;
}

// ── Helpers ──────────────────────────────────────────────────────────────────
function v(id)   { return document.getElementById(id)?.value?.trim() || ''; }
function flt(id) { const n = parseFloat(v(id)); return isNaN(n) ? null : n; }

function _showOverlay(modalId) {
  document.getElementById('modal-overlay').classList.remove('hidden');
  ['modal-login','modal-edit'].forEach(id => {
    document.getElementById(id).classList.toggle('hidden', id !== modalId);
  });
}

function _hideOverlay() {
  document.getElementById('modal-overlay').classList.add('hidden');
  document.getElementById('modal-login').classList.add('hidden');
  document.getElementById('modal-edit').classList.add('hidden');
}

// ── Port navigation + constraints ─────────────────────────────────────────────
// Cached port result — re-applied after the app container becomes visible.
let _portCache = null;   // { lon, lat, zoom, port }

// Called right after #app becomes visible.
// OL initialized with display:none → size=0×0 → map renders half-screen and
// ignores setCenter. Fix: updateSize() tells OL the real container size,
// then re-apply port position so the map lands at the correct place.
function _showMap() {
  requestAnimationFrame(() => {
    window._mapUpdateSize?.();          // fix half-screen rendering
    requestAnimationFrame(() => {
      _applyPortToMap();                // re-apply position after resize
    });
  });
}

function _applyPortToMap() {
  if (!_portCache) return;
  const { lon, lat, zoom, port } = _portCache;

  // Step 1: clear any previous constraints so navigation is free
  window._clearPortConstraints?.();

  // Step 2: fly to port (same as port search)
  window.flyToCoords?.(lon, lat, zoom);

  // Step 3: after animation lands, lock zoom-out and extent
  if (port) {
    setTimeout(() => window._setPortConstraints?.(port), 1400);  // 1200ms anim + margin
  }
}

// Fetch /org/me/company, cache result, apply to map.
// Call this BEFORE showing the app, then re-apply after show (OL resize fix).
async function _fetchAndApplyPort(token, role) {
  try {
    const res = await fetch(`${API}/org/me/company`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    if (!res.ok) return;
    const me = await res.json();
    if (!me.port || me.port.center_lat == null || me.port.center_lon == null) return;

    const isClient = (role === 'USER' || role === 'CLIENT_ADMIN');
    _portCache = {
      lon:  me.port.center_lon,
      lat:  me.port.center_lat,
      zoom: me.port.default_zoom || 12,
      port: isClient ? me.port : null,
    };
    _applyPortToMap();
  } catch (_) { /* non-fatal */ }
}

// ── Login de inicio ───────────────────────────────────────────────────────────
async function initStartupLogin() {
  const screen = document.getElementById('login-screen');
  const app    = document.getElementById('app');
  const btn    = document.getElementById('ls-submit');
  const errEl  = document.getElementById('ls-error');

  // Validate cached token. If valid, configure map THEN show app.
  Auth.load();
  if (Auth.isLoggedIn()) {
    try {
      const r = await fetch(`${API}/auth/me`, {
        headers: { Authorization: `Bearer ${Auth.token()}` },
      });
      if (r.ok) {
        try {
          await _fetchAndApplyPort(Auth.token(), Auth.session.role);
          app.classList.remove('hidden');
          window._mapUpdateSize?.();
          _applyPortToMap();
        } catch (e) {
          console.warn('[auth] map positioning error:', e);
          app.classList.remove('hidden');
        }
        screen.classList.add('hidden');
        afterLogin();
        return;
      }
      Auth.clear();
    } catch { Auth.clear(); }
  }
  screen.classList.remove('hidden');
  app.classList.add('hidden');

  async function doLogin() {
    const username = document.getElementById('ls-user').value.trim();
    const password = document.getElementById('ls-pass').value;
    if (!username || !password) return;

    btn.disabled = true;
    btn.textContent = 'AUTHENTICATING...';
    errEl.classList.add('hidden');

    try {
      const form = new URLSearchParams({ username, password });
      const res  = await fetch(`${API}/auth/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: form,
      });
      if (!res.ok) throw new Error('invalid');
      const data = await res.json();
      Auth.save({ token: data.access_token, username: data.username,
                  nombre: data.nombre, role: data.role });

      // Map positioning is separate — errors here must NOT show credential message
      try {
        await _fetchAndApplyPort(data.access_token, data.role);
        app.classList.remove('hidden');   // show app BEHIND login screen (z-index:9999)
        window._mapUpdateSize?.();        // OL measures real container size
        _applyPortToMap();                // position map at correct port
      } catch (e) {
        console.warn('[auth] map positioning error:', e);
        app.classList.remove('hidden');   // show app anyway at default position
      }
      screen.classList.add('hidden');     // reveal map
      afterLogin();
    } catch {
      errEl.textContent = 'Invalid username or password.';
      errEl.classList.remove('hidden');
    } finally {
      btn.disabled = false;
      btn.textContent = 'LOGIN';
    }
  }

  btn.addEventListener('click', doLogin);
  document.getElementById('ls-pass').addEventListener('keydown', e => {
    if (e.key === 'Enter') doLogin();
  });
}

function afterLogin() {
  if (window.PortSearch) window.PortSearch.init();
  Auth._renderBadge();
  const sbUser = document.getElementById('sb-user');
  if (sbUser && Auth.session) {
    sbUser.textContent = `${Auth.session.nombre}  ·  ${Auth.session.role}`;
  }
}

// ── Event listeners ──────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', () => {
  initStartupLogin();

  document.getElementById('btn-login-submit')?.addEventListener('click', () => Modal.submitLogin());
  document.getElementById('btn-login-cancel')?.addEventListener('click', () => Modal.closeLogin());
  document.getElementById('btn-close-login')?.addEventListener('click',  () => Modal.closeLogin());
  document.getElementById('btn-close-edit')?.addEventListener('click',   () => Modal.closeEdit());
  document.getElementById('btn-edit-cancel')?.addEventListener('click',  () => Modal.closeEdit());

  document.getElementById('login-pass')?.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') Modal.submitLogin();
  });

  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') _hideOverlay();
  });

  // (Click on the backdrop intentionally does NOT close modals — user must
  // use the X or CLOSE button. Avoids losing unsaved Settings inputs.)
});

window.Auth  = Auth;
window.Modal = Modal;
