const credentialStore = require('./credentialStore');
const sessionStore = require('./sessionStore');
const foodsharingClient = require('./foodsharingClient');
const adminConfig = require('./adminConfig');
const { readConfig } = require('./configStore');
const { readStoreWatch, listWatcherProfiles } = require('./storeWatchStore');
const { readStoresCache } = require('./storeCacheStore');
const notificationService = require('./notificationService');
const { scheduleConfig } = require('./pickupScheduler');

const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000;
const WATCHDOG_INTERVAL_MS = 5 * 60 * 1000;
const SESSION_ERROR_COOLDOWN_MS = 6 * 60 * 60 * 1000;

const adminEmail = (process.env.ADMIN_EMAIL || '').toLowerCase();
const profileStates = new Map();
const sessionErrorNotifications = new Map();

let watchdogTimer = null;
let ensureAllInFlight = null;

function isAdmin(profile) {
  return !!adminEmail && !!profile?.email && profile.email.toLowerCase() === adminEmail;
}

function getProfileIds() {
  const ids = new Set();
  Object.keys(credentialStore.loadAll()).forEach((profileId) => ids.add(String(profileId)));
  listWatcherProfiles().forEach((profileId) => ids.add(String(profileId)));
  return Array.from(ids).sort();
}

function getActivePickupEntries(config) {
  return Array.isArray(config) ? config.filter((entry) => entry?.active) : [];
}

function createSignature({ config, watchers, settings }) {
  return JSON.stringify({
    pickup: Array.isArray(config)
      ? config.map((entry) => ({
          id: entry?.id ? String(entry.id) : '',
          active: !!entry?.active,
          hidden: !!entry?.hidden,
          onlyNotify: !!entry?.onlyNotify,
          desiredDate: entry?.desiredDate || null,
          desiredDateRange: entry?.desiredDateRange || null,
          desiredWeekday: entry?.desiredWeekday || null,
          autoDeactivate: entry?.autoDeactivate !== false
        }))
      : [],
    watch: Array.isArray(watchers)
      ? watchers.map((entry) => ({
          storeId: entry?.storeId ? String(entry.storeId) : '',
          lastTeamSearchStatus: entry?.lastTeamSearchStatus ?? null
        }))
      : [],
    settings: {
      pickupFallbackCron: settings?.pickupFallbackCron || null,
      pickupWindowOffsetsMinutes: settings?.pickupWindowOffsetsMinutes || [],
      randomDelayMinSeconds: settings?.randomDelayMinSeconds ?? null,
      randomDelayMaxSeconds: settings?.randomDelayMaxSeconds ?? null,
      storeWatchCron: settings?.storeWatchCron || null,
      storeWatchInitialDelayMinSeconds: settings?.storeWatchInitialDelayMinSeconds ?? null,
      storeWatchInitialDelayMaxSeconds: settings?.storeWatchInitialDelayMaxSeconds ?? null,
      storeWatchRequestDelayMs: settings?.storeWatchRequestDelayMs ?? null,
      storeWatchStatusCacheMaxAgeMinutes: settings?.storeWatchStatusCacheMaxAgeMinutes ?? null
    }
  });
}

function shouldNotifySessionError(profileId) {
  const key = profileId ? String(profileId) : null;
  if (!key) {
    return false;
  }
  const now = Date.now();
  const last = sessionErrorNotifications.get(key) || 0;
  if (now - last < SESSION_ERROR_COOLDOWN_MS) {
    return false;
  }
  sessionErrorNotifications.set(key, now);
  return true;
}

async function notifySessionError(profileId, credentials, error) {
  if (!shouldNotifySessionError(profileId)) {
    return;
  }
  try {
    await notificationService.sendAdminSessionErrorNotification({
      profileId,
      profileEmail: credentials?.email,
      error: error?.message,
      label: 'background-orchestrator'
    });
  } catch (notifyError) {
    console.error('[BACKGROUND] Admin-Session-Fehler konnte nicht gemeldet werden:', notifyError.message);
  }
}

async function loginProfile(profileId, credentials) {
  const auth = await foodsharingClient.login(credentials.email, credentials.password);
  const profile = {
    id: String(auth.profile.id),
    name: auth.profile.name,
    email: auth.profile.email || credentials.email
  };
  const session = sessionStore.create(
    {
      cookieHeader: auth.cookieHeader,
      csrfToken: auth.csrfToken,
      profile,
      credentials,
      isAdmin: isAdmin(profile),
      storesCache: readStoresCache(profile.id)
    },
    credentials.token,
    ONE_YEAR_MS
  );
  credentialStore.save(profile.id, {
    email: credentials.email,
    password: credentials.password,
    token: session.id
  });
  if (String(profileId) !== profile.id) {
    console.warn(`[BACKGROUND] Profil ${profileId} wurde als ${profile.id} angemeldet.`);
  }
  return session;
}

async function ensureProfile(profileId, options = {}) {
  const key = profileId ? String(profileId) : null;
  if (!key) {
    return null;
  }
  const credentials = credentialStore.get(key);
  const previous = profileStates.get(key) || {};

  if (!credentials?.email || !credentials?.password) {
    const state = {
      ...previous,
      profileId: key,
      active: false,
      lastError: 'missing-credentials',
      checkedAt: Date.now()
    };
    profileStates.set(key, state);
    return state;
  }

  try {
    let session = sessionStore.getByProfile(key) || (credentials.token ? sessionStore.get(credentials.token) : null);
    if (!session?.cookieHeader) {
      session = await loginProfile(key, credentials);
      console.log(`[BACKGROUND] Session fuer Profil ${session.profile.id} (${session.profile.name}) bereitgestellt.`);
    } else {
      sessionStore.update(session.id, { credentials });
      session = sessionStore.get(session.id);
    }

    const profileIdForData = String(session.profile.id);
    const config = Array.isArray(options.config) ? options.config : readConfig(profileIdForData);
    const watchers = readStoreWatch(profileIdForData);
    const settings = options.settings || adminConfig.readSettings();
    const signature = createSignature({ config, watchers, settings });
    const hasJobs = Array.isArray(session.jobs) && session.jobs.length > 0;
    const shouldSchedule = options.force || previous.signature !== signature || !hasJobs;

    if (shouldSchedule) {
      scheduleConfig(session.id, config, settings);
      console.log(
        `[BACKGROUND] Jobs fuer Profil ${profileIdForData} sichergestellt ` +
          `(${getActivePickupEntries(config).length} aktive Pickup-Eintraege, ${watchers.length} Watch-Stores).`
      );
    }

    const state = {
      profileId: profileIdForData,
      sessionId: session.id,
      active: true,
      signature,
      checkedAt: Date.now(),
      lastSuccessAt: Date.now(),
      lastError: null,
      activePickupCount: getActivePickupEntries(config).length,
      watchedStoreCount: watchers.length,
      jobCount: sessionStore.get(session.id)?.jobs?.length || 0
    };
    profileStates.set(profileIdForData, state);
    return state;
  } catch (error) {
    console.error(`[BACKGROUND] Jobs fuer Profil ${key} konnten nicht sichergestellt werden:`, error.message);
    await notifySessionError(key, credentials, error);
    const state = {
      ...previous,
      profileId: key,
      active: false,
      checkedAt: Date.now(),
      lastError: error.message || 'unknown-error'
    };
    profileStates.set(key, state);
    return state;
  }
}

async function ensureAll(options = {}) {
  if (ensureAllInFlight) {
    return ensureAllInFlight;
  }
  ensureAllInFlight = (async () => {
    const profileIds = getProfileIds();
    const results = [];
    for (const profileId of profileIds) {
      results.push(await ensureProfile(profileId, options));
    }
    return results;
  })();
  try {
    return await ensureAllInFlight;
  } finally {
    ensureAllInFlight = null;
  }
}

function start(options = {}) {
  if (watchdogTimer) {
    return;
  }
  const intervalMs = Number(options.intervalMs) || WATCHDOG_INTERVAL_MS;
  ensureAll({ force: true }).catch((error) => {
    console.error('[BACKGROUND] Initiale Job-Pruefung fehlgeschlagen:', error.message);
  });
  watchdogTimer = setInterval(() => {
    ensureAll().catch((error) => {
      console.error('[BACKGROUND] Watchdog-Pruefung fehlgeschlagen:', error.message);
    });
  }, intervalMs);
}

function refreshProfile(profileId, options = {}) {
  return ensureProfile(profileId, { ...options, force: true });
}

function refreshAll(options = {}) {
  return ensureAll({ ...options, force: true });
}

function getStatus() {
  return {
    running: !!watchdogTimer,
    profiles: Array.from(profileStates.values()).map((state) => ({ ...state }))
  };
}

module.exports = {
  start,
  refreshProfile,
  refreshAll,
  getStatus
};
