#!/usr/bin/env python3
"""
Custom Criteria Evaluator

Evaluates objective, data-driven criteria for each property:
- Rainfall (from Open-Meteo API)
- Temperature/Growing Season (from Open-Meteo API)
- Airport Distance (calculated from coordinates)

Adds custom_overall_score to analysis_output.csv for integration
with the combined scoring system.
"""

import pandas as pd
import requests
import json
import math
from datetime import datetime
import time

# Import criteria from custom_criteria.py
import sys
sys.path.insert(0, '.')
from custom_criteria import RainfallCriterion, TemperatureCriterion

# Major European airports with coordinates
AIRPORTS = {
    'Amsterdam Schiphol': (52.3105, 4.7683),
    'Paris Charles de Gaulle': (49.0097, 2.5479),
    'Frankfurt': (50.0379, 8.5622),
    'London Heathrow': (51.4700, -0.4543),
    'Brussels': (50.9010, 4.4844),
    'Madrid': (40.4983, -3.5676),
    'Barcelona': (41.2974, 2.0833),
    'Rome Fiumicino': (41.8003, 12.2389),
    'Athens': (37.9364, 23.9445),
    'Lisbon': (38.7742, -9.1342),
    'Copenhagen': (55.6180, 12.6561),
    'Berlin': (52.5666, 13.2884),
    'Munich': (48.3537, 11.7750),
    'Zurich': (47.4582, 8.5556),
    'Vienna': (48.1103, 16.5697),
    'Milan Malpensa': (45.6269, 8.7239),
    'Nice': (43.6584, 7.2159),
    'Lyon': (45.7256, 5.0811),
    'Toulouse': (43.6293, 1.3638),
    'Marseille': (43.4393, 5.2214)
}

def calculate_distance(lat1, lon1, lat2, lon2):
    """
    Calculate distance between two coordinates in kilometers
    Using Haversine formula
    """
    R = 6371  # Earth radius in km

    lat1_rad = math.radians(lat1)
    lat2_rad = math.radians(lat2)
    delta_lat = math.radians(lat2 - lat1)
    delta_lon = math.radians(lon2 - lon1)

    a = math.sin(delta_lat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))

    return R * c

def calculate_nearest_airport_distance(lat, lon):
    """Calculate distance to nearest major airport"""
    if not lat or not lon:
        return None, None

    min_distance = float('inf')
    nearest_airport = None

    for airport_name, (airport_lat, airport_lon) in AIRPORTS.items():
        distance = calculate_distance(lat, lon, airport_lat, airport_lon)
        if distance < min_distance:
            min_distance = distance
            nearest_airport = airport_name

    return nearest_airport, min_distance

def score_airport_distance(distance_km):
    """Convert distance to score (1-5)"""
    if distance_km is None:
        return 3, "No coordinates"

    if distance_km < 60:
        return 5, f"{distance_km:.0f}km - Excellent (< 1hr)"
    elif distance_km < 120:
        return 4, f"{distance_km:.0f}km - Good (1-2hrs)"
    elif distance_km < 180:
        return 3, f"{distance_km:.0f}km - Fair (2-3hrs)"
    elif distance_km < 300:
        return 2, f"{distance_km:.0f}km - Far (3-5hrs)"
    else:
        return 1, f"{distance_km:.0f}km - Very far (5+ hrs)"

def evaluate_property(row, rainfall_criterion, temp_criterion, rate_limit_delay=0.5):
    """
    Evaluate all custom criteria for a property

    Returns:
        dict: {
            'custom_overall_score': float (0-5),
            'rainfall_score': float,
            'temperature_score': float,
            'airport_distance_score': float,
            'rainfall_mm': float,
            'growing_season_days': int,
            'nearest_airport': str,
            'airport_distance_km': float
        }
    """
    lat = row.get('Latitude')
    lon = row.get('Longitude')

    results = {
        'custom_overall_score': 0,
        'rainfall_score': None,
        'temperature_score': None,
        'airport_distance_score': None,
        'rainfall_mm': None,
        'growing_season_days': None,
        'nearest_airport': None,
        'airport_distance_km': None
    }

    # Skip if no coordinates
    if pd.isna(lat) or pd.isna(lon):
        return results

    property_data = {'latitude': lat, 'longitude': lon}
    scores = []
    weights = []

    # 1. Rainfall Criterion
    try:
        time.sleep(rate_limit_delay)  # Rate limiting
        rainfall_result = rainfall_criterion.evaluate(property_data)
        if rainfall_result['score']:
            results['rainfall_score'] = rainfall_result['score']
            results['rainfall_mm'] = rainfall_result['raw_data'].get('annual_rainfall_mm')
            scores.append(rainfall_result['score'])
            weights.append(rainfall_criterion.weight)
    except Exception as e:
        print(f"      ⚠️  Rainfall API error: {e}")

    # 2. Temperature/Growing Season Criterion
    try:
        time.sleep(rate_limit_delay)  # Rate limiting
        temp_result = temp_criterion.evaluate(property_data)
        if temp_result['score']:
            results['temperature_score'] = temp_result['score']
            results['growing_season_days'] = temp_result['raw_data'].get('growing_season_days')
            scores.append(temp_result['score'])
            weights.append(temp_criterion.weight)
    except Exception as e:
        print(f"      ⚠️  Temperature API error: {e}")

    # 3. Airport Distance Criterion
    try:
        nearest_airport, distance_km = calculate_nearest_airport_distance(lat, lon)
        airport_score, reasoning = score_airport_distance(distance_km)

        results['airport_distance_score'] = airport_score
        results['nearest_airport'] = nearest_airport
        results['airport_distance_km'] = distance_km

        scores.append(airport_score)
        weights.append(2.0)  # Weight for airport distance
    except Exception as e:
        print(f"      ⚠️  Airport distance error: {e}")

    # Calculate weighted average
    if scores:
        weighted_sum = sum(s * w for s, w in zip(scores, weights))
        total_weight = sum(weights)
        results['custom_overall_score'] = weighted_sum / total_weight
    else:
        results['custom_overall_score'] = 0

    return results

def main():
    print("🌍 Custom Criteria Evaluator")
    print("=" * 80)
    print()

    # Load data
    try:
        df = pd.read_csv('analysis_output.csv')
        print(f"✓ Loaded {len(df)} properties from analysis_output.csv")
    except FileNotFoundError:
        print("❌ analysis_output.csv not found!")
        return

    # Initialize criteria
    rainfall_criterion = RainfallCriterion()
    temp_criterion = TemperatureCriterion()

    print(f"\n📊 Evaluating custom criteria:")
    print(f"   • {rainfall_criterion.display_name} (weight: {rainfall_criterion.weight})")
    print(f"   • {temp_criterion.display_name} (weight: {temp_criterion.weight})")
    print(f"   • ✈️ Airport Distance (weight: 2.0)")
    print()

    # Count properties with coordinates
    with_coords = df['Latitude'].notna() & df['Longitude'].notna()
    coord_count = with_coords.sum()
    print(f"Properties with coordinates: {coord_count}/{len(df)} ({100*coord_count/len(df):.1f}%)")
    print()

    if coord_count == 0:
        print("❌ No properties have coordinates. Run geocoding first!")
        return

    # Evaluate each property
    print("🔄 Evaluating properties...")
    print("(This may take a few minutes due to API rate limits)")
    print()

    results_list = []
    success_count = 0
    api_error_count = 0

    for idx, row in df.iterrows():
        url = row['URL']
        title = row.get('Titel', 'Untitled')[:50]
        lat = row.get('Latitude')
        lon = row.get('Longitude')

        print(f"[{idx+1}/{len(df)}] {title}")

        if pd.isna(lat) or pd.isna(lon):
            print(f"      ⏭️  No coordinates - skipped")
            results_list.append({})
            continue

        # Evaluate
        results = evaluate_property(row, rainfall_criterion, temp_criterion)

        if results['custom_overall_score'] > 0:
            print(f"      ✓ Custom score: {results['custom_overall_score']:.2f}/5")
            print(f"        Rain: {results['rainfall_score']}/5 ({results['rainfall_mm']:.0f}mm) | "
                  f"Temp: {results['temperature_score']}/5 ({results['growing_season_days']} days) | "
                  f"Airport: {results['airport_distance_score']}/5 ({results['airport_distance_km']:.0f}km to {results['nearest_airport']})")
            success_count += 1
        else:
            print(f"      ⚠️  Could not evaluate (API errors)")
            api_error_count += 1

        results_list.append(results)

    # Add results to dataframe
    print(f"\n💾 Adding custom criteria to analysis_output.csv...")

    df['custom_overall_score'] = [r.get('custom_overall_score', 0) for r in results_list]
    df['rainfall_score'] = [r.get('rainfall_score') for r in results_list]
    df['rainfall_mm'] = [r.get('rainfall_mm') for r in results_list]
    df['temperature_score'] = [r.get('temperature_score') for r in results_list]
    df['growing_season_days'] = [r.get('growing_season_days') for r in results_list]
    df['airport_distance_score'] = [r.get('airport_distance_score') for r in results_list]
    df['airport_distance_km'] = [r.get('airport_distance_km') for r in results_list]
    df['nearest_airport'] = [r.get('nearest_airport') for r in results_list]

    # Save
    df.to_csv('analysis_output.csv', index=False)
    print(f"✓ Saved updated data")

    # Summary
    print(f"\n{'='*80}")
    print(f"✅ EVALUATION COMPLETE")
    print(f"{'='*80}\n")
    print(f"Successfully evaluated: {success_count}/{coord_count} properties")
    print(f"API errors: {api_error_count}")
    print(f"No coordinates: {len(df) - coord_count}")
    print()
    print(f"📊 Custom Criteria Statistics:")

    custom_scores = df[df['custom_overall_score'] > 0]['custom_overall_score']
    if len(custom_scores) > 0:
        print(f"   Average custom score: {custom_scores.mean():.2f}/5")
        print(f"   Min: {custom_scores.min():.2f} | Max: {custom_scores.max():.2f}")

    print()
    print(f"💡 Next steps:")
    print(f"   1. Run: python3 parse_criteria.py")
    print(f"   2. This will combine GPT scores (60%) + Custom scores (40%)")
    print(f"   3. View updated scores in map viewer!")
    print()

if __name__ == '__main__':
    main()
