const foodsharingClient = require('./foodsharingClient');
const sessionStore = require('./sessionStore');
const notificationService = require('./notificationService');

const ADMIN_SESSION_ERROR_COOLDOWN_MS = 6 * 60 * 60 * 1000;
const DEFAULT_TRANSIENT_RETRY_DELAYS_MS = [1000, 3000];
const NON_RETRIABLE_TRANSIENT_LABELS = new Set(['bookSlot']);
const adminSessionErrorCooldowns = new Map();

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function shouldNotifyAdminSessionError(key) {
  if (!key) {
    return false;
  }
  const now = Date.now();
  const lastNotified = adminSessionErrorCooldowns.get(key) || 0;
  if (now - lastNotified < ADMIN_SESSION_ERROR_COOLDOWN_MS) {
    return false;
  }
  adminSessionErrorCooldowns.set(key, now);
  return true;
}

function isUnauthorizedError(error) {
  const status = error?.response?.status;
  if (status === 401) {
    return true;
  }
  if (status !== 403) {
    return false;
  }
  const data = error?.response?.data;
  const message =
    typeof data === 'string'
      ? data
      : typeof data?.message === 'string'
      ? data.message
      : '';
  return message.toLowerCase().includes('not logged in');
}

function isCsrfError(error) {
  const status = error?.response?.status;
  if (status !== 400) {
    return false;
  }
  const data = error?.response?.data;
  const message =
    typeof data === 'string'
      ? data
      : typeof data?.message === 'string'
      ? data.message
      : '';
  return message.toLowerCase().includes('csrf');
}

function isTransientNetworkError(error) {
  if (!error) {
    return false;
  }
  if (error.response) {
    return false;
  }
  const code = String(error.code || '').toUpperCase();
  if (['ECONNRESET', 'ECONNABORTED', 'ETIMEDOUT', 'EPIPE', 'ENOTFOUND', 'EAI_AGAIN'].includes(code)) {
    return true;
  }
  const message = String(error.message || '').toLowerCase();
  return (
    message.includes('socket hang up') ||
    message.includes('timeout') ||
    message.includes('network error') ||
    message.includes('connection reset')
  );
}

async function refreshSession(session, { label } = {}) {
  if (!session?.credentials?.email || !session?.credentials?.password) {
    console.warn(
      `[SESSION] Session ${session?.id || 'unbekannt'} kann nicht erneuert werden – keine Zugangsdaten gespeichert.`
    );
    return false;
  }
  try {
    const refreshed = await foodsharingClient.login(
      session.credentials.email,
      session.credentials.password
    );
    sessionStore.update(session.id, {
      cookieHeader: refreshed.cookieHeader,
      csrfToken: refreshed.csrfToken,
      profile: {
        ...session.profile,
        ...refreshed.profile
      }
    });
    console.log(
      `[SESSION] Session ${session.id} wurde erfolgreich erneuert${label ? ` (${label})` : ''}.`
    );
    return true;
  } catch (error) {
    console.error(
      `[SESSION] Session ${session?.id || 'unbekannt'} konnte nicht erneuert werden${label ? ` (${label})` : ''}:`,
      error.message
    );
    const profileId = session?.profile?.id ? String(session.profile.id) : null;
    const notifyKey = profileId || session?.id || null;
    if (shouldNotifyAdminSessionError(notifyKey)) {
      try {
        await notificationService.sendAdminSessionErrorNotification({
          profileId,
          profileEmail: session?.credentials?.email || session?.profile?.email,
          profileName: session?.profile?.name,
          sessionId: session?.id,
          error: error?.message,
          label
        });
      } catch (notifyError) {
        console.error('[NOTIFY] Admin-Session-Fehler konnte nicht gemeldet werden:', notifyError.message);
      }
    }
    return false;
  }
}

async function ensureSession(session) {
  if (!session?.profile?.id) {
    return false;
  }
  if (!session.cookieHeader) {
    return refreshSession(session, { label: 'missing-cookie' });
  }
  return true;
}

async function withSessionRetry(session, action, { label, transientRetries } = {}) {
  if (!session) {
    throw new Error('Session fehlt');
  }
  if (!session.cookieHeader && session.credentials) {
    const refreshed = await refreshSession(session, { label });
    if (!refreshed) {
      throw new Error('Session konnte nicht erneuert werden');
    }
  }
  const retryDelays = NON_RETRIABLE_TRANSIENT_LABELS.has(label)
    ? []
    : DEFAULT_TRANSIENT_RETRY_DELAYS_MS.slice(
        0,
        Number.isFinite(transientRetries) ? Math.max(0, transientRetries) : DEFAULT_TRANSIENT_RETRY_DELAYS_MS.length
      );
  let transientAttempt = 0;
  while (true) {
    try {
      return await action();
    } catch (error) {
      if (isTransientNetworkError(error) && transientAttempt < retryDelays.length) {
        transientAttempt += 1;
        const delay = retryDelays[transientAttempt - 1];
        console.warn(
          `[SESSION] Transienter Fehler${label ? ` (${label})` : ''}: ${error.message}. ` +
            `Retry ${transientAttempt}/${retryDelays.length} in ${delay}ms.`
        );
        await wait(delay);
        continue;
      }
      if (!isUnauthorizedError(error) && !isCsrfError(error)) {
        throw error;
      }
      const refreshed = await refreshSession(session, { label });
      if (!refreshed) {
        throw error;
      }
      return action();
    }
  }
}

module.exports = {
  ensureSession,
  isTransientNetworkError,
  refreshSession,
  withSessionRetry
};
