#!/usr/bin/env python3
"""
Poll weather stations for Monterey County, CA and Addison County, VT.
Generates individual county maps plus a combined map with layer controls.
"""

import requests
import json
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

# API endpoints
ALL_STATIONS_URL = "https://www.weatherlink.com/map/data"
STATION_DETAIL_URL = "https://www.weatherlink.com/map/data/station/{id}"

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
}

MAX_WORKERS = 5

# County definitions
COUNTIES = {
    'addison': {
        'name': 'Addison County, VT',
        'short_name': 'Addison County',
        'state': 'Vermont',
        'bounds': {
            'min_lat': 43.85,
            'max_lat': 44.25,
            'min_lng': -73.4,
            'max_lng': -72.9
        },
        'center': [44.05, -73.15],
        'zoom': 10,
        'html_file': 'addison_county_weather.html',
        'data_file': 'addison_county_data.json'
    },
    'monterey': {
        'name': 'Monterey County, CA',
        'short_name': 'Monterey County',
        'state': 'California',
        'bounds': {
            'min_lat': 35.78,
            'max_lat': 36.92,
            'min_lng': -121.98,
            'max_lng': -120.21
        },
        'center': [36.35, -121.1],
        'zoom': 9,
        'html_file': 'monterey_county_weather.html',
        'data_file': 'monterey_county_data.json'
    }
}


def is_in_bounds(lat, lng, bounds):
    return (bounds['min_lat'] <= lat <= bounds['max_lat'] and
            bounds['min_lng'] <= lng <= bounds['max_lng'])


def get_temp_color(temp):
    """Get color for temperature value."""
    try:
        t = float(temp)
        if t < 0: return '#29333b'
        elif t < 10: return '#535f79'
        elif t < 20: return '#2f6695'
        elif t < 32: return '#1b7dbf'
        elif t < 40: return '#27a5af'
        elif t < 50: return '#00b58d'
        elif t < 60: return '#009247'
        elif t < 70: return '#fdd03b'
        elif t < 80: return '#ed9922'
        else: return '#dd5626'
    except:
        return '#888888'


def fetch_station_detail(station):
    """Fetch full details for a single station."""
    station_id = station.get('id')
    if not station_id:
        return None

    try:
        url = STATION_DETAIL_URL.format(id=station_id)
        resp = requests.get(url, headers=HEADERS, timeout=10)
        resp.raise_for_status()
        detail = resp.json()

        temp = detail.get('temperature', '--')

        # Filter out bad readings (outside -40 to 130 range)
        try:
            t = float(temp)
            if t < -40 or t > 130:
                temp = '--'
        except:
            pass

        return {
            'lat': station.get('lat'),
            'lng': station.get('lng'),
            'name': detail.get('sStation', 'Unknown'),
            'temp': temp,
            'humidity': detail.get('humidity', '--'),
            'wind': detail.get('windSpeed', '--'),
            'wind_dir': detail.get('windDirection', '--'),
            'barometer': detail.get('barometer', '--'),
            'updated': detail.get('lastUpdatedAt', '--')
        }
    except:
        return None


def poll_county(county_key, all_stations):
    """Poll all stations in a county."""
    county = COUNTIES[county_key]
    bounds = county['bounds']

    print(f"\n--- {county['name']} ---")

    # Filter stations to this county
    county_stations = [s for s in all_stations
                       if is_in_bounds(s.get('lat', 0), s.get('lng', 0), bounds)]
    print(f"  Found {len(county_stations)} stations")

    if not county_stations:
        return [], 0, 0

    # Poll stations in parallel
    stations = []
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        futures = {executor.submit(fetch_station_detail, s): s for s in county_stations}
        for future in as_completed(futures):
            result = future.result()
            if result:
                stations.append(result)

    print(f"  Got data for {len(stations)} stations")

    # Calculate temp stats (excluding bad readings)
    temps = []
    for s in stations:
        try:
            t = float(s['temp'])
            temps.append(t)
        except:
            pass

    if temps:
        min_temp = min(temps)
        max_temp = max(temps)
        print(f"  Temp range: {min_temp:.0f}°F to {max_temp:.0f}°F")
    else:
        min_temp = max_temp = 0

    # Add colors and county tag
    for s in stations:
        s['color'] = get_temp_color(s['temp'])
        s['county'] = county_key

    return stations, min_temp, max_temp


def generate_single_county_html(county_key, stations, min_temp, max_temp):
    """Generate HTML map for a single county."""
    county = COUNTIES[county_key]
    now = datetime.now()

    html = f"""<!DOCTYPE html>
<html>
<head>
    <title>{county['short_name']} Weather</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <style>
        body {{ margin: 0; font-family: system-ui, -apple-system, sans-serif; }}
        #map {{ position: absolute; top: 0; bottom: 0; width: 100%; }}
        .info {{
            padding: 12px 16px;
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,.2);
        }}
        .info h3 {{ margin: 0 0 8px 0; }}
        .legend i {{
            display: inline-block;
            width: 18px;
            height: 18px;
            float: left;
            margin-right: 8px;
            border-radius: 3px;
        }}
    </style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map('map').setView({county['center']}, {county['zoom']});

L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
    attribution: '© OpenStreetMap | Davis Instruments'
}}).addTo(map);

var stations = {json.dumps(stations)};

stations.forEach(function(s) {{
    var m = L.circleMarker([s.lat, s.lng], {{
        radius: 10,
        fillColor: s.color,
        color: '#fff',
        weight: 2,
        fillOpacity: 0.9
    }}).bindPopup(
        '<b>' + s.name + '</b><br>' +
        '🌡️ <b>' + s.temp + '°F</b><br>' +
        '💧 ' + s.humidity + '% humidity<br>' +
        '💨 ' + s.wind + ' mph ' + s.wind_dir + '<br>' +
        '📊 ' + s.barometer + ' in Hg<br>' +
        '<small>' + s.updated + '</small>'
    ).addTo(map);
    m.on('mouseover', function() {{ this.openPopup(); }});
    m.on('mouseout', function() {{ this.closePopup(); }});
}});

var info = L.control({{ position: 'topright' }});
info.onAdd = function() {{
    var div = L.DomUtil.create('div', 'info');
    div.innerHTML = '<h3>🌡️ {county["name"]}</h3>' +
        '<b>{len(stations)}</b> stations<br>' +
        '<small>Range: {min_temp:.0f}°F to {max_temp:.0f}°F</small><br>' +
        '<small>Updated: {now.strftime("%I:%M %p")}</small>';
    return div;
}};
info.addTo(map);

var legend = L.control({{ position: 'bottomright' }});
legend.onAdd = function() {{
    var div = L.DomUtil.create('div', 'info legend');
    div.innerHTML = '<b>Temperature</b><br>' +
        '<i style="background:#2f6695"></i> &lt;20°F<br>' +
        '<i style="background:#1b7dbf"></i> 20-31°F<br>' +
        '<i style="background:#27a5af"></i> 32-39°F<br>' +
        '<i style="background:#00b58d"></i> 40-49°F<br>' +
        '<i style="background:#009247"></i> 50-59°F<br>' +
        '<i style="background:#fdd03b"></i> 60-69°F<br>' +
        '<i style="background:#ed9922"></i> 70°F+';
    return div;
}};
legend.addTo(map);
</script>
</body>
</html>"""

    return html


def generate_combined_html(all_county_data):
    """Generate combined HTML map with both counties and layer controls."""
    now = datetime.now()

    # Combine all stations
    all_stations = []
    county_stats = {}
    for county_key, data in all_county_data.items():
        stations, min_temp, max_temp = data
        all_stations.extend(stations)
        county_stats[county_key] = {
            'count': len(stations),
            'min': min_temp,
            'max': max_temp
        }

    html = f"""<!DOCTYPE html>
<html>
<head>
    <title>Weather Map - Addison & Monterey Counties</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <style>
        body {{ margin: 0; font-family: system-ui, -apple-system, sans-serif; font-size: 16px; }}
        #map {{ position: absolute; top: 0; bottom: 0; width: 100%; }}
        .info {{
            padding: 14px 18px;
            background: #fff;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,.25);
            font-size: 16px;
            line-height: 1.5;
        }}
        .info h3 {{ margin: 0 0 10px 0; font-size: 20px; }}
        .legend {{
            font-size: 15px;
        }}
        .legend i {{
            display: inline-block;
            width: 24px;
            height: 24px;
            float: left;
            margin-right: 10px;
            border-radius: 4px;
        }}
        .legend br {{ line-height: 2; }}
        .county-btn {{
            display: block;
            width: 100%;
            padding: 12px 16px;
            margin: 6px 0;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 16px;
            text-align: left;
        }}
        .county-btn small {{
            font-size: 14px;
        }}
        .county-btn:hover {{ opacity: 0.8; }}
        .btn-addison {{ background: #4a90d9; color: white; }}
        .btn-monterey {{ background: #d97a4a; color: white; }}
        .leaflet-popup-content {{
            font-size: 16px;
            line-height: 1.6;
            margin: 12px 16px;
        }}
        .leaflet-popup-content b {{
            font-size: 17px;
        }}
        .leaflet-popup-content small {{
            font-size: 14px;
        }}
        .leaflet-control-layers {{
            font-size: 15px;
            padding: 8px 12px;
        }}
        .leaflet-control-layers-list {{
            line-height: 2;
        }}
    </style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map('map').setView([39.5, -98.5], 4);

L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
    attribution: '© OpenStreetMap | Davis Instruments'
}}).addTo(map);

var stations = {json.dumps(all_stations)};

// Create layer groups for each county
var addisonLayer = L.layerGroup();
var montereyLayer = L.layerGroup();

stations.forEach(function(s) {{
    var m = L.circleMarker([s.lat, s.lng], {{
        radius: 10,
        fillColor: s.color,
        color: '#fff',
        weight: 2,
        fillOpacity: 0.9
    }}).bindPopup(
        '<b>' + s.name + '</b><br>' +
        '🌡️ <b>' + s.temp + '°F</b><br>' +
        '💧 ' + s.humidity + '% humidity<br>' +
        '💨 ' + s.wind + ' mph ' + s.wind_dir + '<br>' +
        '📊 ' + s.barometer + ' in Hg<br>' +
        '<small>' + s.updated + '</small>'
    );
    m.on('mouseover', function() {{ this.openPopup(); }});
    m.on('mouseout', function() {{ this.closePopup(); }});

    if (s.county === 'addison') {{
        addisonLayer.addLayer(m);
    }} else {{
        montereyLayer.addLayer(m);
    }}
}});

// Add both layers to map
addisonLayer.addTo(map);
montereyLayer.addTo(map);

// Layer control
var overlays = {{
    "Addison County, VT ({county_stats['addison']['count']})": addisonLayer,
    "Monterey County, CA ({county_stats['monterey']['count']})": montereyLayer
}};
L.control.layers(null, overlays, {{ collapsed: false }}).addTo(map);

// Info box with navigation
var info = L.control({{ position: 'topright' }});
info.onAdd = function() {{
    var div = L.DomUtil.create('div', 'info');
    div.innerHTML = '<h3>🌡️ Weather Stations</h3>' +
        '<b>{len(all_stations)}</b> total stations<br><br>' +
        '<button class="county-btn btn-addison" onclick="map.setView([44.05, -73.15], 10)">Addison County, VT<br><small>{county_stats["addison"]["count"]} stations | {county_stats["addison"]["min"]:.0f}°F - {county_stats["addison"]["max"]:.0f}°F</small></button>' +
        '<button class="county-btn btn-monterey" onclick="map.setView([36.35, -121.1], 9)">Monterey County, CA<br><small>{county_stats["monterey"]["count"]} stations | {county_stats["monterey"]["min"]:.0f}°F - {county_stats["monterey"]["max"]:.0f}°F</small></button>' +
        '<br><small>Updated: {now.strftime("%I:%M %p")}</small>';
    return div;
}};
info.addTo(map);

// Legend
var legend = L.control({{ position: 'bottomright' }});
legend.onAdd = function() {{
    var div = L.DomUtil.create('div', 'info legend');
    div.innerHTML = '<b>Temperature</b><br>' +
        '<i style="background:#2f6695"></i> &lt;20°F<br>' +
        '<i style="background:#1b7dbf"></i> 20-31°F<br>' +
        '<i style="background:#27a5af"></i> 32-39°F<br>' +
        '<i style="background:#00b58d"></i> 40-49°F<br>' +
        '<i style="background:#009247"></i> 50-59°F<br>' +
        '<i style="background:#fdd03b"></i> 60-69°F<br>' +
        '<i style="background:#ed9922"></i> 70°F+';
    return div;
}};
legend.addTo(map);
</script>
</body>
</html>"""

    return html


def main():
    print("=" * 60)
    print("County Weather Map Poller")
    print(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 60)

    # Fetch all stations once
    print("\nFetching station list...")
    resp = requests.get(ALL_STATIONS_URL, headers=HEADERS, timeout=30)
    all_stations = resp.json()
    print(f"  Total worldwide: {len(all_stations):,}")

    # Poll each county and store results
    all_county_data = {}

    for county_key in COUNTIES:
        county = COUNTIES[county_key]

        stations, min_temp, max_temp = poll_county(county_key, all_stations)
        all_county_data[county_key] = (stations, min_temp, max_temp)

        if stations:
            # Generate and save individual county HTML
            html = generate_single_county_html(county_key, stations, min_temp, max_temp)
            with open(county['html_file'], 'w') as f:
                f.write(html)
            print(f"  ✓ Saved {county['html_file']}")

            # Save data JSON
            with open(county['data_file'], 'w') as f:
                json.dump(stations, f, indent=2)
            print(f"  ✓ Saved {county['data_file']}")

    # Generate combined map
    print("\n--- Combined Map ---")
    combined_html = generate_combined_html(all_county_data)
    with open('weather_map.html', 'w') as f:
        f.write(combined_html)
    print("  ✓ Saved weather_map.html")

    # Save combined data
    all_stations_combined = []
    for county_key, data in all_county_data.items():
        all_stations_combined.extend(data[0])
    with open('all_stations_data.json', 'w') as f:
        json.dump(all_stations_combined, f, indent=2)
    print("  ✓ Saved all_stations_data.json")

    print("\n" + "=" * 60)
    print(f"Completed: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 60)


if __name__ == "__main__":
    main()
