import { LeadProvider } from './LeadProvider';
import { Lead, SearchQuery } from '../types';

export class OverpassProvider implements LeadProvider {
  name = 'OpenStreetMap (Overpass)';

  async search(query: SearchQuery): Promise<Lead[]> {
    const { keyword, location } = query;

    // First use Nominatim to geocode the location
    // Use an identifying User-Agent as per Nominatim Usage Policy
    const userAgent = 'LeadForge-App/1.0 (https://github.com/leadforge; support@leadforge.example.com)';
    
    const nomRes = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(location)}&format=json&limit=1`, {
      headers: { 'User-Agent': userAgent }
    });
    
    if (!nomRes.ok) {
      const errorText = await nomRes.text();
      console.error(`Nominatim API Error (${nomRes.status}):`, errorText);
      return [];
    }
    
    const nomData = await nomRes.json();
    console.log(`Nominatim results for "${location}":`, nomData.length);
    if (!nomData || !nomData.length) {
      console.log('Location not found in OpenStreetMap:', location);
      return [];
    }
    
    const bbox = nomData[0].boundingbox;
    console.log(`BBox for "${location}":`, bbox);
    // bounding box: [lat_min, lat_max, lon_min, lon_max] -> usually [south, north, west, east]
    let south = parseFloat(bbox[0]);
    let north = parseFloat(bbox[1]);
    let west = parseFloat(bbox[2]);
    let east = parseFloat(bbox[3]);
    
    // Safety check: If the bounding box is too large (e.g. searching for a whole country),
    // Overpass will either timeout or return empty. We shrink it to a reasonable radius around the center.
    const latSpan = north - south;
    const lonSpan = east - west;
    
    if (latSpan > 0.5 || lonSpan > 0.5) {
      console.log(`Large search area detected (${latSpan.toFixed(2)}x${lonSpan.toFixed(2)}). Shrinking BBox for performance.`);
      const centerLat = (north + south) / 2;
      const centerLon = (east + west) / 2;
      // Shrink to ~20km box (0.2 degrees)
      south = centerLat - 0.1;
      north = centerLat + 0.1;
      west = centerLon - 0.1;
      east = centerLon + 0.1;
    }

    const cleanKeyword = keyword.trim();
    
    // Use Overpass API to find places matching the keyword inside the bounding box
    // Optimization: Search for specific tags first (fast/indexed) and then by name
    const overpassQuery = `
      [out:json][timeout:15];
      (
        // Search by amenity/shop tags (usually indexed)
        nwr[amenity~"${cleanKeyword}",i](${south},${west},${north},${east});
        nwr[shop~"${cleanKeyword}",i](${south},${west},${north},${east});
        nwr[office~"${cleanKeyword}",i](${south},${west},${north},${east});
        // Fallback to name search
        nwr[name~"${cleanKeyword}",i](${south},${west},${north},${east});
      );
      out center 150;
    `;
    
    const endpoints = [
      'https://overpass-api.de/api/interpreter',
      'https://lz4.overpass-api.de/api/interpreter',
      'https://z.overpass-api.de/api/interpreter'
    ];
    
    console.log('Executing Overpass Query:', overpassQuery);
    
    let osmDataText = '';
    let success = false;

    for (const endpoint of endpoints) {
      try {
        console.log(`Trying Overpass endpoint: ${endpoint}`);
        const osmRes = await fetch(endpoint, {
          method: 'POST',
          body: 'data=' + encodeURIComponent(overpassQuery),
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'User-Agent': userAgent
          }
        });
        
        osmDataText = await osmRes.text();
        if (osmRes.ok && !osmDataText.includes('rate_limited') && !osmDataText.includes('Error')) {
          success = true;
          break;
        } else {
          console.warn(`Endpoint ${endpoint} failed or was rate limited.`);
        }
      } catch (error) {
        console.error(`Error fetching from ${endpoint}:`, error);
      }
    }
    
    try {
      if (!success) {
        console.error('All Overpass endpoints failed or were rate limited.');
        return [];
      }

      console.log(`Overpass Response (first 200 chars): ${osmDataText.substring(0, 200)}`);

      let osmData;
      try {
        osmData = JSON.parse(osmDataText);
      } catch (err) {
        console.error('Failed to parse Overpass response even though it seemed successful.');
        return [];
      }

      if (!osmData.elements) {
        console.log('No elements found in Overpass response');
        return [];
      }

      console.log(`Found ${osmData.elements.length} elements from Overpass`);

      return osmData.elements.map((e: any) => {
        const tags = e.tags || {};
        
        // Ensure URLs are absolute format
        let website = tags.website || tags['contact:website'] || tags.url || tags['contact:url'] || '';
        if (website && !website.startsWith('http')) {
          website = 'https://' + website;
        }

        const addressFields = [
            tags['addr:housenumber'], 
            tags['addr:street'], 
            tags['addr:city'], 
            tags['addr:state'], 
            tags['addr:postcode']
        ].filter(Boolean);

        const fallbackAddress = location.charAt(0).toUpperCase() + location.slice(1);
        
        return {
          id: `osm-${e.type}-${e.id}`,
          name: tags.name || 'Unnamed Business',
          email: tags.email || tags['contact:email'] || '',
          phone: tags.phone || tags['contact:phone'] || '',
          website: website,
          address: addressFields.length > 0 ? addressFields.join(', ') : fallbackAddress,
          industry: tags.amenity || tags.shop || tags.office || keyword,
          source: this.name,
          qualityScore: calculateQualityScore(tags, addressFields.length)
        };
      });
    } catch (error) {
      console.error('Error fetching from Overpass API:', error);
      return [];
    }
  }
}

function calculateQualityScore(tags: any, addressLength: number): number {
  let score = 30; // base score just for existing
  
  if (tags.phone || tags['contact:phone']) score += 20;
  if (tags.email || tags['contact:email']) score += 30;
  if (tags.website || tags['contact:website']) score += 20;
  if (addressLength > 0) score += 15;
  if (tags.opening_hours) score += 10;
  
  return Math.min(score, 100);
}
