// Simple front-end auth helper
// Responsibilities: login, logout, token storage, auth header, role checks, guards
// Integrates with auth-bootstrap.js for race-condition-free operation

const auth = (function () {
  const TOKEN_KEY = 'token';
  const USER_KEY = 'user';
  const EXP_KEY = 'token_exp';
  // Disabled only when server debug / suspend-timeouts is on (see loadSessionTimingSettings)
  let DEV_NO_TIMEOUT = false;
  if (window.DEV_DISABLE_TIMEOUT === true) {
    DEV_NO_TIMEOUT = true;
    console.log('[Auth] DEV_DISABLE_TIMEOUT flag present -> disabling inactivity timeout');
  }

  const POS_PAGE_PATTERN = /pos\.html|pos_new\.html|pos-reconciliation\.html/i;

  function isPosPage() {
    try {
      return POS_PAGE_PATTERN.test(window.location.pathname || '');
    } catch (_) {
      return false;
    }
  }

  function isPosUser() {
    const u = getUser();
    const role = (u && u.role ? String(u.role) : '').toLowerCase();
    return role === 'cashier' || role === 'pos_user';
  }

  function isPosContext() {
    return isPosPage() || isPosUser();
  }

  function nowSec() { return Math.floor(Date.now() / 1000); }

  function saveSession(token, payload, apiResponse) {
    const resp = apiResponse || {};
    localStorage.setItem(TOKEN_KEY, token);
    localStorage.setItem(USER_KEY, JSON.stringify({
      id: payload.sub,
      role: payload.role || resp.role,
      username: payload.username || resp.username,
      branch_id: resp.branch_id || payload.branch_id,
      branch_code: resp.branch_code || payload.branch_code,
      branch_name: resp.branch_name || null,
      company_id: resp.company_id || payload.company_id || null,
      company_code: resp.company_code || null,
      company_name: resp.company_name || null,
      can_switch_branches: !!resp.can_switch_branches,
      cnp_support: !!resp.cnp_support,
    }));
    if (payload.exp) localStorage.setItem(EXP_KEY, payload.exp);
    if (resp.branch_id) {
      try { localStorage.setItem('selected_branch_id', resp.branch_id); } catch (_) { }
    }
  }

  function _loginEndpoint() {
    if (window.DEV_USE_LOGIN_DIRECT === true) {
      return '/api/v1/auth/login-direct';
    }
    return '/api/v1/auth/login-json';
  }

  function _applyLoginResponse(data) {
    const payload = parseJwt(data.access_token);
    payload.role = data.role;
    payload.username = data.username;
    payload.branch_id = data.branch_id;
    payload.branch_code = data.branch_code;
    payload.company_id = data.company_id;
    saveSession(data.access_token, payload, data);

    broadcastSessionUpdate('login', {
      token: data.access_token,
      user: {
        id: payload.sub,
        role: payload.role,
        username: payload.username,
        branch_id: data.branch_id,
        branch_code: data.branch_code,
        branch_name: data.branch_name,
        company_id: data.company_id,
        company_code: data.company_code,
        company_name: data.company_name,
        cnp_support: data.cnp_support,
      },
      exp: payload.exp,
    });

    if (data.must_change_password) {
      window.location.replace('/static/change-password.html');
      return data;
    }

    const role = (data.role || '').toLowerCase();
    if (role === 'cashier' || role === 'pos_user') {
      window.location.replace('/static/pos.html');
    }

    updateUserDisplayInNavbar();
    startTokenValidityCheck();
    return data;
  }

  function parseJwt(token) {
    try {
      const base64Url = token.split('.')[1];
      const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
      const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
      }).join(''));
      return JSON.parse(jsonPayload);
    } catch { return {}; }
  };

  // Token validity check interval (60 seconds)
  let tokenCheckInterval = null;

  async function checkTokenValidity() {
    const token = getToken();
    if (!token) return false;

    try {
      const response = await fetch('/api/v1/auth/check-token', {
        headers: {
          'Authorization': `Bearer ${token}`
        }
      });
      const data = await response.json();

      if (!data.valid) {
        console.log('[auth] 🚫 Token is no longer valid:', data.reason);
        // Token revoked - logout all tabs
        logout('token_revoked');
        return false;
      }
      return true;
    } catch (e) {
      console.warn('[auth] Token check failed:', e);
      return true; // Assume valid on network error
    }
  }

  function startTokenValidityCheck() {
    // Check token validity every 60 seconds
    if (tokenCheckInterval) clearInterval(tokenCheckInterval);

    tokenCheckInterval = setInterval(async () => {
      if (isAuthenticated()) {
        await checkTokenValidity();
      }
    }, 60000); // 60 seconds

    console.log('[auth] 🔄 Token validity check started (60s interval)');
  }

  function stopTokenValidityCheck() {
    if (tokenCheckInterval) {
      clearInterval(tokenCheckInterval);
      tokenCheckInterval = null;
    }
  }

  const WORKSPACE_KEY = 'cnp_login_workspace';

  function getLoginWorkspace() {
    try {
      const raw = localStorage.getItem(WORKSPACE_KEY);
      return raw ? JSON.parse(raw) : null;
    } catch {
      return null;
    }
  }

  function saveLoginWorkspace(ws) {
    if (!ws || !ws.branch_id) return;
    try {
      localStorage.setItem(WORKSPACE_KEY, JSON.stringify({
        company_code: ws.company_code || null,
        company_name: ws.company_name || null,
        branch_id: ws.branch_id,
        branch_code: ws.branch_code || null,
        branch_name: ws.branch_name || null,
      }));
    } catch (_) { /* ignore */ }
  }

  function clearLoginWorkspace() {
    try { localStorage.removeItem(WORKSPACE_KEY); } catch (_) { }
  }

  async function login(username, password, opts) {
    const options = opts || {};
    const payload = { username, password };
    if (options.branch_id) payload.branch_id = options.branch_id;
    const bc = (options.branch_code || '').trim();
    if (bc) payload.branch_code = bc;
    const cc = (options.company_code || '').trim();
    if (cc) payload.company_code = cc;

    const endpoint = _loginEndpoint();
    const res = await fetch(endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });

    if (!res.ok) {
      let msg = 'Invalid credentials';
      try {
        const err = await res.json();
        msg = err.detail || (Array.isArray(err.detail) ? err.detail.map(d => d.msg || d).join(', ') : msg) || msg;
      } catch (_) { /* ignore */ }
      throw new Error(typeof msg === 'string' ? msg : 'Login failed');
    }

    const data = await res.json();
    saveLoginWorkspace({
      company_code: data.company_code,
      company_name: data.company_name,
      branch_id: data.branch_id,
      branch_code: data.branch_code,
      branch_name: data.branch_name,
    });
    return _applyLoginResponse(data);
  }

  function logout(reason) {
    try {
      // Stop token validity check
      stopTokenValidityCheck();

      if (reason) {
        try { localStorage.setItem('logout_reason', reason); } catch { }
      }

      // Broadcast logout to other tabs before clearing local storage
      broadcastSessionUpdate('logout');

      // Force storage event by toggling a logout flag
      try {
        localStorage.setItem('logout_trigger', Date.now().toString());
        setTimeout(() => localStorage.removeItem('logout_trigger'), 100);
      } catch { }

      // Remove known keys
      localStorage.removeItem(TOKEN_KEY);
      localStorage.removeItem(USER_KEY);
      localStorage.removeItem(EXP_KEY);
      localStorage.removeItem('current_pos_session_id');
      localStorage.removeItem('selected_branch_id');
      // Do NOT clear all localStorage so reason survives
      try { sessionStorage.clear(); } catch { }
    } catch (e) { console.warn('Logout cleanup issue', e); }

    // Small delay to ensure storage events are fully propagated to other tabs
    setTimeout(() => {
      if (window.CNP_ERP && typeof window.CNP_ERP.redirectAfterLogout === 'function') {
        window.CNP_ERP.redirectAfterLogout();
      } else {
        window.location.replace('/static/login.html');
      }
    }, 50);
  }

  function getToken() { return localStorage.getItem(TOKEN_KEY); }
  function getUser() { try { return JSON.parse(localStorage.getItem(USER_KEY) || 'null'); } catch { return null; } }
  function isAuthenticated() {
    // Use bootstrap's validation if available (more robust)
    if (window.authBootstrap && typeof window.authBootstrap.isAuthenticated === 'function') {
      return window.authBootstrap.isAuthenticated();
    }
    // Fallback to local validation
    const t = getToken();
    if (!t) return false;
    const exp = parseInt(localStorage.getItem(EXP_KEY) || '0', 10);
    if (exp && nowSec() > exp) { logout(); return false; }
    return true;
  }
  function authHeader() {
    const t = getToken();
    const u = getUser();
    const headers = t ? { 'Authorization': 'Bearer ' + t } : {};
    // Include X-User-ID header for audit trail tracking
    if (u && u.id) {
      headers['X-User-ID'] = u.id;
      headers['X-Username'] = u.username || '';
    }
    return headers;
  }
  function requireAuth() {
    if (!isAuthenticated()) {
      if (window.CNP_ERP && typeof window.CNP_ERP.redirectWhenUnauthenticated === 'function') {
        window.CNP_ERP.redirectWhenUnauthenticated();
      } else {
        window.location.replace('/static/login.html');
      }
      return false;
    }
    return true;
  }
  function hasRole(roles) { const u = getUser(); if (!u) return false; if (!Array.isArray(roles)) roles = [roles]; return roles.includes(u.role) || u.role === 'super_admin' || u.role === 'accountant'; }

  // Role-based redirection
  function redirectBasedOnRole() {
    const user = getUser();
    if (!user) return;

    const role = (user.role || '').toLowerCase();
    const currentPath = window.location.pathname;

    // POS users should only access POS pages
    if (role === 'cashier' || role === 'pos_user') {
      // If they're not on a POS page, redirect them
      if (!currentPath.includes('pos.html') && !currentPath.includes('login.html')) {
        window.location.replace('/static/pos.html');
        return;
      }
    }
  }

  // Update user display in navbar
  function updateUserDisplayInNavbar() {
    // Try to update user display if navbar loader is available
    if (window.navbarLoader && typeof window.navbarLoader.updateUserDisplay === 'function') {
      window.navbarLoader.updateUserDisplay();
    }
    // Also try the standalone function
    if (typeof updateUserDisplay === 'function') {
      updateUserDisplay();
    }
  }

  // Inactivity monitor — ERP default 30 min, POS default 5 min (overridden by Settings → Security)
  const DEFAULT_ERP_IDLE_MINUTES = 30;
  const DEFAULT_POS_IDLE_MINUTES = 5;
  let ERP_IDLE_MINUTES = DEFAULT_ERP_IDLE_MINUTES;
  let POS_IDLE_MINUTES = DEFAULT_POS_IDLE_MINUTES;
  let INACTIVITY_LIMIT_MS = DEFAULT_ERP_IDLE_MINUTES * 60 * 1000;
  let lastActivity = Date.now();
  let lastRefreshOnActivity = 0;
  function recordActivity() {
    lastActivity = Date.now();
    // Proactively refresh JWT while the user is working (avoids surprise 401 logouts)
    if (!DEV_NO_TIMEOUT && isAuthenticated()) {
      const exp = parseInt(localStorage.getItem(EXP_KEY) || '0', 10);
      const minutesLeft = exp ? (exp - nowSec()) / 60 : 999;
      if (minutesLeft < 25 && Date.now() - lastRefreshOnActivity > 60000) {
        lastRefreshOnActivity = Date.now();
        maybeRefreshToken();
      }
    }
  }
  ['click', 'mousemove', 'keydown', 'touchstart', 'scroll', 'focus'].forEach(evt => {
    window.addEventListener(evt, recordActivity, { passive: true });
  });
  document.addEventListener('visibilitychange', () => { if (!document.hidden) recordActivity(); });

  function resolveInactivityLimitMs() {
    const minutes = isPosContext() ? POS_IDLE_MINUTES : ERP_IDLE_MINUTES;
    return Math.max(1, minutes) * 60 * 1000;
  }

  function applyInactivityLimitFromSettings() {
    INACTIVITY_LIMIT_MS = resolveInactivityLimitMs();
  }

  function getWarnLeadMs(limitMs) {
    const limitMin = limitMs / 60000;
    if (limitMin <= 5) {
      return Math.min(60000, Math.max(10000, limitMs * 0.25));
    }
    const leadMin = Math.min(IDLE_WARNING_MINUTES, Math.max(1, limitMin - 1));
    return leadMin * 60 * 1000;
  }

  let REFRESH_THRESHOLD_MIN = 10;
  let IDLE_WARNING_MINUTES = 1;

  (async function loadSessionTimingSettings() {
    try {
      const res = await fetch('/api/v1/settings/');
      if (!res.ok) return;
      const json = await res.json();
      const security = json?.data?.security || {};
      const general = json?.data?.general || {};

      if (general.debug_mode === true || json?.data?.debug_mode === true) {
        DEV_NO_TIMEOUT = true;
        console.log('[Auth] Debug mode: inactivity logout disabled');
      }

      const erpMin = parseInt(security.session_timeout_minutes, 10)
        || parseInt(json?.data?.session_timeout, 10)
        || parseInt(general.session_timeout, 10);
      if (!isNaN(erpMin) && erpMin > 0) {
        ERP_IDLE_MINUTES = erpMin;
      }

      const posMin = parseInt(security.pos_session_timeout_minutes, 10);
      if (!isNaN(posMin) && posMin > 0) {
        POS_IDLE_MINUTES = posMin;
      }

      if (security.refresh_threshold_minutes && !isNaN(security.refresh_threshold_minutes)) {
        REFRESH_THRESHOLD_MIN = security.refresh_threshold_minutes;
      }
      if (security.idle_warning_minutes && !isNaN(security.idle_warning_minutes)) {
        IDLE_WARNING_MINUTES = security.idle_warning_minutes;
      }

      applyInactivityLimitFromSettings();
      const label = isPosContext() ? 'POS' : 'ERP';
      console.log('[Auth]', label, 'idle timeout:', (INACTIVITY_LIMIT_MS / 60000), 'minutes',
        '(ERP:', ERP_IDLE_MINUTES, 'min, POS:', POS_IDLE_MINUTES, 'min)');
    } catch (e) { /* ignore */ }
  })();
  let refreshing = false;
  async function maybeRefreshToken() {
    try {
      if (refreshing) return;
      const exp = parseInt(localStorage.getItem(EXP_KEY) || '0', 10);
      if (!exp) return;
      const now = nowSec();
      const minutesLeft = (exp - now) / 60;
      const idle = Date.now() - lastActivity;
      if (DEV_NO_TIMEOUT) return; // token already long-lived
      if (minutesLeft < REFRESH_THRESHOLD_MIN && idle < (INACTIVITY_LIMIT_MS - 60000)) {
        refreshing = true;
        const resp = await fetch('/api/v1/auth/refresh', { method: 'POST', headers: authHeader() });
        if (resp.ok) {
          const data = await resp.json();
          const payload = parseJwt(data.access_token);
          payload.role = data.role; payload.username = data.username; payload.branch_id = data.branch_id; payload.branch_code = data.branch_code;
          saveSession(data.access_token, payload);
          if (window.console) console.log('[Auth] Token refreshed');
          try { broadcastSessionUpdate('refresh', { exp: payload.exp }); } catch { }
        } else {
          if (window.console) console.warn('[Auth] Token refresh failed', resp.status);
        }
        refreshing = false;
      }
    } catch (e) { refreshing = false; }
  }
  setInterval(maybeRefreshToken, 120000);

  function runInactivityCheck() {
    try {
      if (!isAuthenticated()) return;
      applyInactivityLimitFromSettings();
      const idle = Date.now() - lastActivity;
      if (DEV_NO_TIMEOUT) return;
      if (idle > INACTIVITY_LIMIT_MS) {
        console.warn('[Auth] Inactivity logout triggered', {
          idleMs: idle,
          limitMs: INACTIVITY_LIMIT_MS,
          context: isPosContext() ? 'pos' : 'erp'
        });
        logout('Session expired due to inactivity');
        return;
      }
      // Yellow "Session Expiring" warning modal removed.
      // We keep the hard inactivity logout above, but do not show the pre-logout UI.
    } catch (e) { /* silent */ }
  }

  setInterval(runInactivityCheck, 5000);

  // Expose debug info helper
  function debugInfo() {
    return {
      tokenPresent: !!getToken(),
      exp: parseInt(localStorage.getItem(EXP_KEY) || '0', 10),
      nowSec: nowSec(),
      minutesLeft: (function () { const exp = parseInt(localStorage.getItem(EXP_KEY) || '0', 10); return exp ? ((exp - nowSec()) / 60) : null; })(),
      inactivityLimitMinutes: INACTIVITY_LIMIT_MS / 60000,
      erpIdleMinutes: ERP_IDLE_MINUTES,
      posIdleMinutes: POS_IDLE_MINUTES,
      isPosContext: isPosContext(),
      devNoTimeout: DEV_NO_TIMEOUT,
      lastActivityAgeSec: (Date.now() - lastActivity) / 1000,
      refreshThresholdMin: typeof REFRESH_THRESHOLD_MIN !== 'undefined' ? REFRESH_THRESHOLD_MIN : undefined
    };
  }

  // Cross-tab session persistence
  const SESSION_UPDATE_KEY = 'auth_session_update';
  const LAST_ACTIVITY_KEY = 'auth_last_activity';

  // Function to broadcast session changes across tabs
  function broadcastSessionUpdate(action, data = {}) {
    try {
      const update = {
        action,
        timestamp: Date.now(),
        tabId: Math.random().toString(36).substr(2, 9),
        ...data
      };
      localStorage.setItem(SESSION_UPDATE_KEY, JSON.stringify(update));
      // Immediately remove to trigger storage event in other tabs
      setTimeout(() => localStorage.removeItem(SESSION_UPDATE_KEY), 100);
    } catch (e) {
      console.warn('Failed to broadcast session update:', e);
    }
  }

  // Function to update last activity across tabs
  function updateCrossTabActivity() {
    try {
      localStorage.setItem(LAST_ACTIVITY_KEY, Date.now().toString());
    } catch (e) {
      console.warn('Failed to update cross-tab activity:', e);
    }
  }

  // Listen for session updates from other tabs
  window.addEventListener('storage', function (e) {
    // Handle explicit logout trigger
    if (e.key === 'logout_trigger' && e.newValue) {
      console.log('[auth] 🚪 Logout detected from another tab');
      if (!window.location.pathname.includes('login.html') && !window.location.pathname.includes('logout.html')) {
        window.location.replace('/static/login.html');
      }
      return;
    }

    // Handle token removal (logout)
    if (e.key === TOKEN_KEY && e.oldValue && !e.newValue) {
      console.log('[auth] 🚪 Token removed - logging out this tab');
      if (!window.location.pathname.includes('login.html') && !window.location.pathname.includes('logout.html')) {
        window.location.replace('/static/login.html');
      }
      return;
    }

    if (e.key === SESSION_UPDATE_KEY && e.newValue) {
      try {
        const update = JSON.parse(e.newValue);
        console.log('[auth] Received session update from another tab:', update.action);

        switch (update.action) {
          case 'login':
            // Another tab logged in, refresh our session state
            if (update.token && update.user) {
              localStorage.setItem(TOKEN_KEY, update.token);
              localStorage.setItem(USER_KEY, JSON.stringify(update.user));
              if (update.exp) localStorage.setItem(EXP_KEY, update.exp);
              // Update navbar display
              updateUserDisplayInNavbar();
              // Reset activity timer
              lastActivity = Date.now();
            }
            break;

          case 'logout':
            // Another tab logged out, clear our session
            localStorage.removeItem(TOKEN_KEY);
            localStorage.removeItem(USER_KEY);
            localStorage.removeItem(EXP_KEY);
            localStorage.removeItem('current_pos_session_id');
            localStorage.removeItem('selected_branch_id');
            // Redirect to login if not already there
            if (!window.location.pathname.includes('login.html')) {
              window.location.replace('/static/login.html');
            }
            break;

          case 'activity':
            // Another tab had activity, update our timer
            lastActivity = Math.max(lastActivity, update.timestamp);
            break;
        }
      } catch (err) {
        console.warn('Failed to process session update:', err);
      }
    }

    // Also listen for activity updates from other tabs
    if (e.key === LAST_ACTIVITY_KEY && e.newValue) {
      try {
        const otherActivity = parseInt(e.newValue, 10);
        lastActivity = Math.max(lastActivity, otherActivity);
      } catch (err) {
        console.warn('Failed to process activity update:', err);
      }
    }
  });

  // Override recordActivity to broadcast across tabs
  const originalRecordActivity = recordActivity;
  recordActivity = function () {
    originalRecordActivity();
    updateCrossTabActivity();
  };

  async function switchBranch(branch_id) {
    if (!isAuthenticated()) throw new Error('Not authenticated');
    const res = await fetch('/api/v1/auth/switch-branch', { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeader() }, body: JSON.stringify({ branch_id }) });
    if (!res.ok) { let msg = 'Branch switch failed'; try { const e = await res.json(); msg = e.detail || msg; } catch { } throw new Error(msg); }
    const data = await res.json();
    const payload = parseJwt(data.access_token);
    payload.role = data.role; payload.username = data.username; payload.branch_id = data.branch_id; payload.branch_code = data.branch_code;
    saveSession(data.access_token, payload);

    // Broadcast branch switch to other tabs
    broadcastSessionUpdate('login', {
      token: data.access_token,
      user: { id: payload.sub, role: payload.role, username: payload.username, branch_id: payload.branch_id, branch_code: payload.branch_code },
      exp: payload.exp
    });

    updateUserDisplayInNavbar();
    return data;
  }

  function applyLoginResponse(data) {
    return _applyLoginResponse(data);
  }

  return {
    login, logout, getToken, getUser, isAuthenticated, authHeader, requireAuth, hasRole,
    redirectBasedOnRole, switchBranch, updateUserDisplayInNavbar, debugInfo,
    getLoginWorkspace, saveLoginWorkspace, clearLoginWorkspace, recordActivity,
    applyLoginResponse,
  };
})();

// Auto-attach to window
window.auth = auth;
// Provide a robust global logout() for inline onclick="logout()" usage
// Always assign to ensure stale definitions get overridden consistently
window.logout = function (reason) {
  try {
    if (window.navbarLoader && typeof window.navbarLoader.handleLogout === 'function') {
      // Use enhanced confirm/logout flow when available
      window.navbarLoader.handleLogout();
      return;
    }
  } catch (_) { }
  // Fallback to direct auth logout
  try { auth.logout(reason); } catch (e) { try { window.location.replace('/static/login.html'); } catch (_) { } }
};
// Signal to other scripts (e.g., navbar-loader) that auth is ready
try { document.dispatchEvent(new Event('authReady')); } catch (_) { }
