const https = require('https');
const fs = require('fs').promises;
const path = require('path');
const config = require('../config/env');
const logger = require('./logger');

const CACHE_FILE = path.join(__dirname, '..', 'geocode-cache.json');
const CACHE_SAVE_INTERVAL = 60000; // Save every 60 seconds

const cache = new Map();
let lastRequestTime = 0;
let cacheModified = false;

// Load cache from file on startup
const loadCache = async () => {
  try {
    const data = await fs.readFile(CACHE_FILE, 'utf8');
    const parsed = JSON.parse(data);
    Object.entries(parsed).forEach(([key, value]) => {
      cache.set(key, value);
    });
    logger.info(`Geocoding cache loaded: ${cache.size} entries`);
  } catch (error) {
    if (error.code !== 'ENOENT') {
      logger.warn('Failed to load geocoding cache:', error.message);
    }
  }
};

// Save cache to file
const saveCache = async () => {
  if (!cacheModified) return;
  
  try {
    const obj = Object.fromEntries(cache);
    await fs.writeFile(CACHE_FILE, JSON.stringify(obj, null, 2));
    cacheModified = false;
    logger.info(`Geocoding cache saved: ${cache.size} entries`);
  } catch (error) {
    logger.error('Failed to save geocoding cache:', error.message);
  }
};

// Periodically save cache
setInterval(saveCache, CACHE_SAVE_INTERVAL);

// Save on process exit
process.on('SIGINT', async () => {
  await saveCache();
  process.exit(0);
});
process.on('SIGTERM', async () => {
  await saveCache();
  process.exit(0);
});

// Initialize cache loading
loadCache().catch(err => logger.error('Cache initialization error:', err));

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

const fetchJson = (url, headers) => new Promise((resolve, reject) => {
  const req = https.get(url, { headers }, (res) => {
    let data = '';
    res.on('data', chunk => {
      data += chunk;
    });
    res.on('end', () => {
      if (res.statusCode && res.statusCode >= 400) {
        return reject(new Error(`Geocode HTTP ${res.statusCode}`));
      }
      try {
        resolve(JSON.parse(data));
      } catch (error) {
        reject(error);
      }
    });
  });

  req.on('error', reject);
  req.end();
});

const geocodeAddress = async (address) => {
  const normalized = (address || '').trim();
  if (!normalized) {
    return null;
  }

  const cacheKey = normalized.toLowerCase();
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }

  const elapsed = Date.now() - lastRequestTime;
  if (elapsed < config.geocodeMinDelayMs) {
    await sleep(config.geocodeMinDelayMs - elapsed);
  }

  const url = `${config.geocodeUrl}?format=json&limit=1&countrycodes=de&q=${encodeURIComponent(normalized)}`;
  const headers = {
    'User-Agent': config.geocodeUserAgent
  };

  try {
    const results = await fetchJson(url, headers);
    lastRequestTime = Date.now();

    if (!Array.isArray(results) || results.length === 0) {
      cache.set(cacheKey, null);
      cacheModified = true;
      return null;
    }

    const hit = results[0];
    const lat = parseFloat(hit.lat);
    const lng = parseFloat(hit.lon);

    if (Number.isNaN(lat) || Number.isNaN(lng)) {
      cache.set(cacheKey, null);
      cacheModified = true;
      return null;
    }

    const coords = { lat, lng };
    cache.set(cacheKey, coords);
    cacheModified = true;
    return coords;
  } catch (error) {
    logger.warn('Geocoding fehlgeschlagen', {
      address: normalized,
      error: error.message
    });
    lastRequestTime = Date.now();
    return null;
  }
};

module.exports = {
  geocodeAddress
};
