const axios = require('axios');
const http = require('http');
const https = require('https');
const requestLogStore = require('./requestLogStore');
const sessionStore = require('./sessionStore');
const maintenanceMode = require('./maintenanceMode');

const BASE_URL = 'https://foodsharing.de';

const keepAliveHttpAgent = new http.Agent({ keepAlive: true, maxSockets: 10 });
const keepAliveHttpsAgent = new https.Agent({ keepAlive: true, maxSockets: 10 });

const client = axios.create({
  baseURL: BASE_URL,
  timeout: 20000,
  httpAgent: keepAliveHttpAgent,
  httpsAgent: keepAliveHttpsAgent,
  headers: {
    'User-Agent': 'pickup-config/1.0 (+https://foodsharing.de)',
    Accept: 'application/json, text/plain, */*'
  }
});

client.interceptors.request.use((config) => {
  const metadata = config.metadata && typeof config.metadata === 'object' ? config.metadata : {};
  config.metadata = { ...metadata, startedAt: Date.now() };
  return config;
});

client.interceptors.response.use(
  (response) => {
    const startedAt = response?.config?.metadata?.startedAt || Date.now();
    const metadata = response?.config?.metadata || {};
    if (!metadata.skipRequestLog) {
      try {
        requestLogStore.add({
          direction: 'outgoing',
          target: 'foodsharing.de',
          method: (response.config?.method || 'GET').toUpperCase(),
          path: response.config?.url || '',
          status: response.status,
          durationMs: Date.now() - startedAt,
          sessionId: metadata.sessionId ?? null,
          profileId: metadata.profileId ?? null,
          profileName: metadata.profileName ?? null,
          responseBody: response.data
        });
      } catch (error) {
        console.warn('[REQUEST-LOG] Outgoing-Log fehlgeschlagen:', error.message);
      }
    }
    updateSessionCookiesFromResponse(response);
    return response;
  },
  (error) => {
    const startedAt = error?.config?.metadata?.startedAt || Date.now();
    const metadata = error?.config?.metadata || {};
    let logEntry = null;
    if (!metadata.skipRequestLog) {
      try {
        logEntry = requestLogStore.add({
          direction: 'outgoing',
          target: 'foodsharing.de',
          method: (error.config?.method || 'GET').toUpperCase(),
          path: error.config?.url || '',
          status: error?.response?.status || null,
          durationMs: Date.now() - startedAt,
          sessionId: metadata.sessionId ?? null,
          profileId: metadata.profileId ?? null,
          profileName: metadata.profileName ?? null,
          error: error?.message || 'Unbekannter Fehler',
          responseBody: error?.response?.data
        });
      } catch (logError) {
        console.warn('[REQUEST-LOG] Outgoing-Error-Log fehlgeschlagen:', logError.message);
      }
      try {
        maintenanceMode.recordOutboundFailure(logEntry || {
          direction: 'outgoing',
          target: 'foodsharing.de',
          status: error?.response?.status || null,
          error: error?.message || 'Unbekannter Fehler',
          timestamp: Date.now()
        });
      } catch (maintenanceError) {
        console.warn('[MAINTENANCE] Fehlerauswertung fehlgeschlagen:', maintenanceError.message);
      }
    }
    if (error?.response) {
      updateSessionCookiesFromResponse(error.response);
    }
    return Promise.reject(error);
  }
);

const CSRF_COOKIE_NAMES = ['FS_CSRF_TOKEN', 'CSRF_TOKEN', 'CSRF-TOKEN', 'XSRF-TOKEN', 'XSRF_TOKEN'];

function extractCookieValue(cookies = [], name) {
  if (!Array.isArray(cookies) || !name) {
    return null;
  }
  const prefix = `${name}=`;
  const match = cookies.find((cookie) => cookie.startsWith(prefix));
  if (!match) {
    return null;
  }
  return match.split(';')[0].slice(prefix.length);
}

function extractCsrfToken(cookies = []) {
  for (const name of CSRF_COOKIE_NAMES) {
    const value = extractCookieValue(cookies, name);
    if (value) {
      return value;
    }
  }
  return null;
}

function extractCsrfTokenFromCookieHeader(cookieHeader = '') {
  if (!cookieHeader) {
    return null;
  }
  const pairs = cookieHeader.split(';').map((part) => part.trim());
  for (const name of CSRF_COOKIE_NAMES) {
    const prefix = `${name}=`;
    const match = pairs.find((pair) => pair.startsWith(prefix));
    if (match) {
      return match.slice(prefix.length);
    }
  }
  return null;
}

function serializeCookies(cookies = []) {
  if (!Array.isArray(cookies)) {
    return '';
  }
  return cookies.map((c) => c.split(';')[0]).join('; ');
}

function buildHeaders(cookieHeader, csrfToken) {
  const headers = {};
  if (cookieHeader) {
    headers.cookie = cookieHeader;
  }
  const token = csrfToken || extractCsrfTokenFromCookieHeader(cookieHeader);
  if (token) {
    headers['x-csrf-token'] = token;
    headers['x-xsrf-token'] = token;
  }
  return headers;
}

function buildRequestMetadata(context) {
  if (!context) {
    return {};
  }
  if (context.sessionId || context.profileId) {
    return {
      sessionId: context.sessionId ?? null,
      profileId: context.profileId ?? null,
      profileName: context.profileName ?? null
    };
  }
  if (context.id || context.profile?.id) {
    return {
      sessionId: context.id ?? null,
      profileId: context.profile?.id ?? null,
      profileName: context.profile?.name ?? null
    };
  }
  return {};
}

function buildRequestConfig({ cookieHeader, csrfToken, context, params } = {}) {
  const metadata = buildRequestMetadata(context);
  const config = {
    headers: buildHeaders(cookieHeader, csrfToken)
  };
  if (Object.keys(metadata).length > 0) {
    config.metadata = metadata;
  }
  if (params) {
    config.params = params;
  }
  return config;
}

function mergeCookieHeaders(existingHeader = '', setCookies = []) {
  const cookieMap = new Map();
  if (existingHeader) {
    existingHeader.split(';').forEach((part) => {
      const trimmed = part.trim();
      if (!trimmed) {
        return;
      }
      const [name, ...valueParts] = trimmed.split('=');
      if (!name) {
        return;
      }
      cookieMap.set(name, valueParts.join('='));
    });
  }

  if (Array.isArray(setCookies)) {
    setCookies.forEach((cookie) => {
      if (!cookie || typeof cookie !== 'string') {
        return;
      }
      const [nameValue] = cookie.split(';');
      if (!nameValue) {
        return;
      }
      const [name, ...valueParts] = nameValue.split('=');
      if (!name) {
        return;
      }
      cookieMap.set(name, valueParts.join('='));
    });
  }

  return Array.from(cookieMap.entries())
    .map(([name, value]) => `${name}=${value}`)
    .join('; ');
}

function updateSessionCookiesFromResponse(response) {
  const setCookies = response?.headers?.['set-cookie'];
  if (!Array.isArray(setCookies) || setCookies.length === 0) {
    return;
  }
  const metadata = response?.config?.metadata || {};
  const sessionId = metadata.sessionId;
  if (!sessionId) {
    return;
  }
  const session = sessionStore.get(sessionId);
  if (!session) {
    return;
  }
  const mergedCookieHeader = mergeCookieHeaders(session.cookieHeader, setCookies);
  if (!mergedCookieHeader) {
    return;
  }
  const csrfToken = extractCsrfTokenFromCookieHeader(mergedCookieHeader) || session.csrfToken || null;
  sessionStore.update(sessionId, {
    cookieHeader: mergedCookieHeader,
    csrfToken
  });
}

async function getCurrentUserDetails(cookieHeader, context, options = {}) {
  const response = await client.get(
    '/api/users/current/details',
    buildRequestConfig({ cookieHeader, context })
  );
  return options.raw ? response : response.data;
}

async function login(email, password) {
  const payload = {
    email,
    password,
    code: '',
    rememberMe: true
  };

  const headers = {
    Origin: BASE_URL,
    Referer: BASE_URL,
    'Cache-Control': 'no-cache, no-store, must-revalidate',
    Pragma: 'no-cache',
    Expires: '0',
    'Content-Type': 'application/json; charset=utf-8'
  };

  const response = await client.post('/api/login', payload, { headers });
  const cookies = response.headers['set-cookie'] || [];
  const csrfToken = extractCsrfToken(cookies);
  let cookieHeader = mergeCookieHeaders('', cookies);
  const detailsResponse = await getCurrentUserDetails(cookieHeader, null, { raw: true });
  const detailsCookies = detailsResponse?.headers?.['set-cookie'] || [];
  if (detailsCookies.length > 0) {
    cookieHeader = mergeCookieHeaders(cookieHeader, detailsCookies);
  }
  const updatedCsrf = extractCsrfTokenFromCookieHeader(cookieHeader) || csrfToken;
  const details = detailsResponse?.data;
  if (!details?.id) {
    throw new Error('Profil-ID konnte nicht ermittelt werden.');
  }

  const nameParts = [];
  if (details.firstname) {
    nameParts.push(details.firstname);
  }
  if (details.lastname) {
    nameParts.push(details.lastname);
  }

  return {
    csrfToken: updatedCsrf,
    cookieHeader,
    profile: {
      id: String(details.id),
      name: nameParts.length > 0 ? nameParts.join(' ') : details.email || email,
      email: details.email || email
    }
  };
}

async function fetchProfile(cookieHeader, { throwOnError = false } = {}, context) {
  try {
    return await getCurrentUserDetails(cookieHeader, context);
  } catch (error) {
    if (throwOnError) {
      throw error;
    }
    console.warn('Profil konnte nicht geladen werden:', error.message);
    return null;
  }
}

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

function hasRegularStoreAccess(store) {
  if (!store) {
    return false;
  }
  if (store.isManaging) {
    return true;
  }
  const membershipStatus = Number(store.membershipStatus);
  if (!Number.isFinite(membershipStatus)) {
    return true;
  }
  return membershipStatus === 1;
}

async function fetchStores(cookieHeader, profileId, options = {}, context) {
  if (!profileId) {
    return [];
  }
  const delayBetweenRequestsMs = Number.isFinite(options.delayBetweenRequestsMs)
    ? Math.max(0, options.delayBetweenRequestsMs)
    : 0;
  const onStoreCheck =
    typeof options.onStoreCheck === 'function'
      ? options.onStoreCheck
      : null;
  try {
    const response = await client.get(
      `/api/users/${profileId}/stores`,
      buildRequestConfig({ cookieHeader, params: { excludeInactive: true }, context })
    );
    const stores = Array.isArray(response.data) ? response.data : [];
    const normalized = stores.map((store) => ({
      id: String(store.id),
      name: store.name || `Store ${store.id}`,
      pickupStatus: store.pickupStatus,
      membershipStatus: store.membershipStatus,
      isManaging: !!store.isManaging,
      city: store.city || '',
      street: store.street || '',
      zip: store.zipCode || store.zip || ''
    }));

    return annotateStoresWithPickupSlots(
      normalized,
      cookieHeader,
      delayBetweenRequestsMs,
      onStoreCheck,
      context
    );
  } catch (error) {
    console.warn('Stores konnten nicht geladen werden:', error.message);
    return [];
  }
}

async function annotateStoresWithPickupSlots(
  stores,
  cookieHeader,
  delayBetweenRequestsMs = 0,
  onStoreCheck,
  context
) {
  if (!Array.isArray(stores) || stores.length === 0) {
    return [];
  }

  const delayMs = Number.isFinite(delayBetweenRequestsMs) ? Math.max(0, delayBetweenRequestsMs) : 0;
  const annotated = [];

  for (let index = 0; index < stores.length; index += 1) {
    const store = stores[index];
    if (onStoreCheck) {
      try {
        onStoreCheck(store, index + 1, stores.length);
      } catch (callbackError) {
        console.warn('Store-Progress-Callback fehlgeschlagen:', callbackError);
      }
    }
    if (delayMs > 0 && index > 0) {
      await wait(delayMs);
    }
    let hasPickupSlots = null;
    if (!hasRegularStoreAccess(store)) {
      hasPickupSlots = false;
      console.info(
        `Pickups für Store ${store.id} werden wegen Mitgliedschaftsstatus ${store.membershipStatus} nicht abgefragt.`
      );
      annotated.push({ ...store, hasPickupSlots });
      continue;
    }
    try {
      const pickups = await fetchPickups(store.id, cookieHeader, context);
      hasPickupSlots = Array.isArray(pickups) && pickups.length > 0;
    } catch (error) {
      const status = error?.response?.status;
      if (status === 403) {
        hasPickupSlots = false;
        console.warn(`Pickups für Store ${store.id} nicht erlaubt (403) – wird als ohne Slots behandelt.`);
      } else {
        console.warn(`Pickups für Store ${store.id} konnten nicht geprüft werden:`, error.message);
      }
    }
    annotated.push({ ...store, hasPickupSlots });
  }

  return annotated;
}

async function fetchPickups(storeId, cookieHeader, context) {
  const response = await client.get(
    `/api/stores/${storeId}/pickups`,
    buildRequestConfig({ cookieHeader, context })
  );
  if (Array.isArray(response.data)) {
    return response.data;
  }
  return response.data?.pickups || [];
}

async function fetchRegionStores(regionId, cookieHeader, context) {
  if (!regionId) {
    return { total: 0, stores: [] };
  }
  const response = await client.get(
    `/api/regions/${regionId}/stores`,
    buildRequestConfig({ cookieHeader, context })
  );
  const stores = Array.isArray(response.data) ? response.data : Array.isArray(response.data?.stores) ? response.data.stores : [];
  return {
    total: Number(response.data?.total) || stores.length,
    stores
  };
}

async function fetchStoreMarker(storeId, cookieHeader, context) {
  if (!storeId) {
    return null;
  }
  const response = await client.get(
    `/api/map/markers/stores/${storeId}`,
    buildRequestConfig({ cookieHeader, context })
  );
  return response.data || null;
}

async function pickupRuleCheck(storeId, utcDate, profileId, session) {
  const response = await client.get(
    `/api/stores/${storeId}/pickups/${utcDate}/eligibility`,
    buildRequestConfig({
      cookieHeader: session.cookieHeader,
      csrfToken: session.csrfToken,
      context: session
    })
  );
  return response.data?.isEligible === true || response.data?.result === true;
}

async function fetchStoreMembers(storeId, cookieHeader, context) {
  if (!storeId) {
    return [];
  }
  const response = await client.get(
    `/api/stores/${storeId}/members`,
    buildRequestConfig({ cookieHeader, context })
  );
  return Array.isArray(response.data) ? response.data : [];
}

async function fetchRegularPickup(storeId, cookieHeader, context) {
  if (!storeId) {
    return [];
  }
  const response = await client.get(
    `/api/stores/${storeId}/regular-pickups`,
    buildRequestConfig({ cookieHeader, context })
  );
  return Array.isArray(response.data) ? response.data : [];
}

async function fetchRegisteredPickups(userId, cookieHeader, context) {
  const normalizedUserId = userId ? String(userId) : 'current';
  const response = await client.get(
    `/api/users/${normalizedUserId}/pickups/registered`,
    buildRequestConfig({ cookieHeader, context })
  );
  return Array.isArray(response.data) ? response.data : [];
}

async function fetchConversations(cookieHeader, options = {}, context) {
  const limit = Math.max(1, Math.min(Number(options.limit) || 20, 100));
  const offset = Math.max(0, Number(options.offset) || 0);
  const config = buildRequestConfig({ cookieHeader, params: { limit, offset }, context });
  config.metadata = { ...(config.metadata || {}), skipRequestLog: true };
  const response = await client.get('/api/conversations', config);
  const conversations = Array.isArray(response.data?.conversations)
    ? response.data.conversations
    : Array.isArray(response.data)
    ? response.data
    : [];
  return {
    conversations,
    profiles: Array.isArray(response.data?.profiles) ? response.data.profiles : []
  };
}

async function bookSlot(storeId, utcDate, profileId, session) {
  await client.post(
    `/api/stores/${storeId}/pickups/${utcDate}/users/current`,
    {},
    {
      ...buildRequestConfig({
        cookieHeader: session.cookieHeader,
        csrfToken: session.csrfToken,
        context: session
      })
    }
  );
}

module.exports = {
  login,
  fetchProfile,
  fetchStores,
  fetchPickups,
  fetchRegionStores,
  fetchStoreMarker,
  fetchStoreMembers,
  fetchRegularPickup,
  fetchRegisteredPickups,
  fetchConversations,
  pickupRuleCheck,
  bookSlot
};
