import axios from 'axios';
import redisClient from './redis-client.js';
import * as appMetrics from './src/metrics.js';

const CIMA_API_BASE_URL = 'https://cima.aemps.es/cima/rest';
const CACHE_TTL = 3600;

/**
 * Wraps a CIMA API GET with duration + success/error metrics.
 */
async function trackedCimaGet(url, params) {
  const start = Date.now();
  try {
    const response = await axios.get(url, { params, timeout: 5000 });
    appMetrics.cimaRequestDuration.record(Date.now() - start);
    appMetrics.cimaRequestsTotal.add(1, { status: 'ok' });
    return response;
  } catch (err) {
    appMetrics.cimaRequestDuration.record(Date.now() - start);
    appMetrics.cimaRequestsTotal.add(1, { status: 'error' });
    throw err;
  }
}

/**
 * Separa los términos de dosis (ej: "1g", "1000 mg") del nombre del
 * medicamento, para poder buscar en CIMA solo con el nombre y luego
 * filtrar por dosis con mayor precisión.
 */
function parseSearchQuery(query) {
  const terms = query.trim().toLowerCase().split(/\s+/).filter(Boolean);

  const dosagePattern = /^\d+(g|mg|mcg|µg|ml|ui|%)$/i;
  const dosageTerms = [];
  const nameTerms = [];

  for (const term of terms) {
    const clean = term.replace(/\s+/g, '');
    if (dosagePattern.test(clean)) {
      dosageTerms.push(clean);
    } else {
      nameTerms.push(term);
    }
  }

  return { nameQuery: nameTerms.join(' '), dosageTerms };
}

/**
 * Filtra los resultados de CIMA comprobando que todos los términos del
 * nombre aparezcan en el nombre comercial o principio activo, y que los
 * términos de dosis coincidan con el campo dosage normalizado.
 */
function filterMedicinesByFullQuery(medicines, searchTerm) {
  const parsed = parseSearchQuery(searchTerm);
  const nameTerms = parsed.nameQuery.split(/\s+/).filter(Boolean);
  const dosageTerms = parsed.dosageTerms;

  if (nameTerms.length === 0 && dosageTerms.length === 0) return medicines;

  return medicines.filter((m) => {
    if (nameTerms.length > 0) {
      const hay = `${m.name || ''} ${m.active_ingredient || ''}`.toLowerCase();
      if (!nameTerms.every((term) => hay.includes(term))) return false;
    }

    if (dosageTerms.length > 0) {
      const dosageHay = (m.dosage || '').toLowerCase().replace(/\s+/g, '');
      if (!dosageTerms.every((term) => dosageHay.includes(term))) return false;
    }

    return true;
  });
}

/**
 * Busca medicamentos en la API de CIMA con caché de Redis
 * @param {string} query - Término de búsqueda
 * @returns {Promise<Array>} - Lista de medicamentos encontrados
 */
export async function searchMedicines(query) {
  if (!query || query.trim().length < 2) {
    return [];
  }

  const searchTerm = query.trim().toLowerCase();
  const cacheKey = `medicines:search:v3:${searchTerm}`;

  try {
    appMetrics.medicineSearchesTotal.add(1);

    // Intentar obtener del caché
    const cachedData = await redisClient.get(cacheKey);
    
    if (cachedData) {
      console.log(`📦 Cache hit for: ${searchTerm}`);
      appMetrics.cacheHitsTotal.add(1);
      return JSON.parse(cachedData);
    }

    appMetrics.cacheMissesTotal.add(1);
    const parsed = parseSearchQuery(searchTerm);
    const apiSearchTerm = parsed.nameQuery || searchTerm;

    console.log(`🌐 Fetching from CIMA API: ${apiSearchTerm}`);
    const response = await trackedCimaGet(`${CIMA_API_BASE_URL}/medicamentos`, {
      nombre: apiSearchTerm,
    });

    if (response.data && response.data.resultados) {
      // Transformar los datos de CIMA a nuestro formato
      const medicines = response.data.resultados.map(med => ({
        id: med.nregistro,
        nregistro: med.nregistro,
        name: med.nombre,
        active_ingredient: med.vtm?.nombre || null,
        dosage: med.dosis || null,
        form: med.formaFarmaceutica?.nombre || null,
        formSimplified: med.formaFarmaceuticaSimplificada?.nombre || null,
        laboratory: med.labtitular,
        prescription: med.cpresc,
        commercialized: med.comerc,
        generic: med.generico,
        photos: med.fotos || [],
        docs: med.docs || []
      }));

      const filtered = filterMedicinesByFullQuery(medicines, searchTerm);

      // Guardar en caché
      await redisClient.setEx(cacheKey, CACHE_TTL, JSON.stringify(filtered));
      
      console.log(`✅ Cached ${filtered.length} medicines for: ${searchTerm}`);
      return filtered;
    }

    return [];
  } catch (error) {
    console.error('Error searching medicines from CIMA:', error.message);
    
    // Si falla, intentar devolver datos cacheados aunque hayan expirado
    try {
      const staleData = await redisClient.get(cacheKey);
      if (staleData) {
        console.log('⚠️ Returning stale cache data due to API error');
        return JSON.parse(staleData);
      }
    } catch (cacheError) {
      console.error('Cache fallback also failed:', cacheError);
    }
    
    return [];
  }
}

/**
 * Obtiene detalles de un medicamento específico por su número de registro
 * @param {string} nregistro - Número de registro del medicamento
 * @returns {Promise<Object|null>} - Datos del medicamento
 */
export async function getMedicineDetails(nregistro) {
  const cacheKey = `medicine:${nregistro}`;

  try {
    appMetrics.medicineSearchesTotal.add(1);

    // Intentar obtener del caché
    const cachedData = await redisClient.get(cacheKey);
    
    if (cachedData) {
      console.log(`📦 Cache hit for medicine: ${nregistro}`);
      appMetrics.cacheHitsTotal.add(1);
      return JSON.parse(cachedData);
    }

    appMetrics.cacheMissesTotal.add(1);

    // Intentar obtener de la API de CIMA (endpoint individual puede no existir)
    console.log(`🌐 Fetching medicine details from CIMA: ${nregistro}`);
    try {
      const response = await trackedCimaGet(`${CIMA_API_BASE_URL}/medicamento/${nregistro}`);

      if (response.data) {
        const med = response.data;
        const medicineDetails = {
          id: med.nregistro,
          nregistro: med.nregistro,
          name: med.nombre,
          active_ingredient: med.principiosActivos?.[0]?.nombre || med.vtm?.nombre || null,
          dosage: med.dosis || null,
          form: med.formaFarmaceutica?.nombre || null,
          formSimplified: med.formaFarmaceuticaSimplificada?.nombre || null,
          laboratory: med.labtitular,
          prescription: med.cpresc,
          commercialized: med.comerc,
          generic: med.generico,
          photos: med.fotos || [],
          docs: med.docs || [],
          presentations: med.presentaciones || []
        };

        await redisClient.setEx(cacheKey, CACHE_TTL * 24, JSON.stringify(medicineDetails));
        return medicineDetails;
      }
    } catch (apiError) {
      console.log(`⚠️ Individual medicine endpoint failed, trying search fallback`);
    }

    // Fallback: buscar por nregistro usando el endpoint de búsqueda
    const searchResponse = await trackedCimaGet(`${CIMA_API_BASE_URL}/medicamentos`, {
      params: { nregistro },
    });

    if (searchResponse.data?.resultados?.length > 0) {
      const med = searchResponse.data.resultados[0];
      const medicineDetails = {
        id: med.nregistro,
        nregistro: med.nregistro,
        name: med.nombre,
        active_ingredient: med.vtm?.nombre || null,
        dosage: med.dosis || null,
        form: med.formaFarmaceutica?.nombre || null,
        formSimplified: med.formaFarmaceuticaSimplificada?.nombre || null,
        laboratory: med.labtitular,
        prescription: med.cpresc,
        commercialized: med.comerc,
        generic: med.generico,
        photos: med.fotos || [],
        docs: med.docs || []
      };

      await redisClient.setEx(cacheKey, CACHE_TTL * 24, JSON.stringify(medicineDetails));
      return medicineDetails;
    }

    return null;
  } catch (error) {
    console.error(`Error fetching medicine ${nregistro} from CIMA:`, error.message);
    
    // Intentar devolver datos cacheados aunque hayan expirado
    try {
      const staleData = await redisClient.get(cacheKey);
      if (staleData) {
        console.log('⚠️ Returning stale cache data due to API error');
        return JSON.parse(staleData);
      }
    } catch (cacheError) {
      console.error('Cache fallback also failed:', cacheError);
    }
    
    return null;
  }
}

/**
 * Busca medicamentos OTC (Sin Receta) en la API de CIMA con caché de Redis
 * @param {string} query - Término de búsqueda
 * @returns {Promise<Array>} - Lista de medicamentos OTC encontrados
 */
export async function searchOTC(query) {
  if (!query || query.trim().length < 2) {
    return [];
  }

  const searchTerm = query.trim().toLowerCase();
  const cacheKey = `cima:otc:${searchTerm}`;

  try {
    // Intentar obtener del caché
    const cachedData = await redisClient.get(cacheKey);
    
    if (cachedData) {
      console.log(`📦 Cache hit for OTC search: ${searchTerm}`);
      return JSON.parse(cachedData);
    }

    const parsed = parseSearchQuery(searchTerm);
    const apiSearchTerm = parsed.nameQuery || searchTerm;

    console.log(`🌐 Fetching OTC from CIMA API: ${apiSearchTerm}`);
    const response = await axios.get(`${CIMA_API_BASE_URL}/medicamentos`, {
      params: {
        nombre: apiSearchTerm,
        cpresc: 'Sin Receta'
      },
      timeout: 5000
    });

    if (response.data && response.data.resultados) {
      // Transformar los datos de CIMA al modelo unificado Product
      const medicines = response.data.resultados.map(med => ({
        id: med.nregistro,
        source: 'cima',
        name: med.nombre,
        brand: med.labtitular || '',
        category: 'otc',
        image_url: med.fotos?.[0]?.url || null,
        active_ingredient: med.vtm?.nombre || null,
        dosage: med.dosis || null,
        form: med.formaFarmaceutica?.nombre || null,
        prescription: med.cpresc,
        commercialized: med.comerc,
        photos: med.fotos || [],
        docs: med.docs || []
      }));

      const filtered = filterMedicinesByFullQuery(medicines, searchTerm);

      // Guardar en caché (1h TTL)
      await redisClient.setEx(cacheKey, CACHE_TTL, JSON.stringify(filtered));
      
      console.log(`✅ Cached ${filtered.length} OTC medicines for: ${searchTerm}`);
      return filtered;
    }

    return [];
  } catch (error) {
    console.error('Error searching OTC medicines from CIMA:', error.message);
    
    // Si falla, intentar devolver datos cacheados aunque hayan expirado
    try {
      const staleData = await redisClient.get(cacheKey);
      if (staleData) {
        console.log('⚠️ Returning stale OTC cache data due to API error');
        return JSON.parse(staleData);
      }
    } catch (cacheError) {
      console.error('OTC cache fallback also failed:', cacheError);
    }
    
    return [];
  }
}

/**
 * Limpia el caché de búsquedas (útil para testing o mantenimiento)
 * @param {string} pattern - Patrón de claves a eliminar (ej: 'medicines:*')
 * @returns {Promise<number>} - Número de claves eliminadas
 */
export async function clearCache(pattern = 'medicines:*') {
  try {
    const keys = await redisClient.keys(pattern);
    if (keys.length > 0) {
      await redisClient.del(keys);
      console.log(`🗑️ Cleared ${keys.length} cache entries`);
      return keys.length;
    }
    return 0;
  } catch (error) {
    console.error('Error clearing cache:', error);
    return 0;
  }
}
