#!/usr/bin/env python3
"""
Saskatchewan HVAC Distance Verification
Pulls actual driving distances from OSRM (Open Source Routing Machine)
"""

import urllib.request
import json
import time
from dataclasses import dataclass
from typing import Dict, Tuple, List

# All locations with coordinates (we'll verify these too)
# Format: name, lat, lng
locations = [
    # Shops
    ("Regina Shop", 50.4452, -104.6189),
    ("Saskatoon Shop", 52.1332, -106.6700),

    # Sites
    ("Lumsden", 50.6519, -104.8569),
    ("Regina ReStore", 50.4518, -104.5912),  # 1740 Broder St
    ("Regina Butterfield", 50.4665, -104.5657),  # 455 Henderson Dr
    ("Bulyea", 50.9851, -104.8698),
    ("Strasbourg", 51.0719, -104.9519),
    ("Nokomis", 51.5047, -105.0045),
    ("Jansen", 51.9220, -104.7165),
    ("Moose Jaw", 50.3928, -105.5419),
    ("Swift Current", 50.2881, -107.7939),
    ("Yorkton", 51.2139, -102.4628),
    ("Saskatoon LINN", 52.1532, -106.6664),  # 1801 Ontario Ave
    ("Saskatoon Old TT", 52.1422, -106.5999),  # 1212 Central Ave
    ("Casa Rio", 52.0251, -106.6245),
    ("Neuanlage", 52.4544, -106.4654),
    ("Waldheim", 52.6228, -106.6476),
    ("Rosthern", 52.6668, -106.3237),
    ("Clavet", 51.9401, -106.3800),
    ("Cardinal Estates", 51.9043, -106.5012),
    ("Shields", 51.8238, -106.4066),
    ("Allan", 51.8933, -106.0555),
    ("Young", 51.7797, -105.7620),
    ("Prud'Homme", 52.3210, -105.9291),
    ("Humboldt", 52.2024, -105.1259),
    ("Tarnopol", 52.7263, -105.3781),
    ("Prince Albert", 53.2033, -105.7531),
]

def get_osrm_distance(lat1, lng1, lat2, lng2) -> Tuple[float, float]:
    """
    Get driving distance and duration from OSRM
    Returns (distance_km, duration_hours)
    """
    url = f"http://router.project-osrm.org/route/v1/driving/{lng1},{lat1};{lng2},{lat2}?overview=false"

    try:
        with urllib.request.urlopen(url, timeout=10) as response:
            data = json.loads(response.read().decode())
            if data["code"] == "Ok" and data["routes"]:
                route = data["routes"][0]
                distance_km = route["distance"] / 1000
                duration_hours = route["duration"] / 3600
                return (round(distance_km, 1), round(duration_hours, 2))
    except Exception as e:
        print(f"  Error: {e}")

    return (None, None)

def build_distance_matrix():
    """Build a matrix of distances between all locations"""
    n = len(locations)
    distances = {}

    print("=" * 80)
    print("FETCHING ACTUAL DRIVING DISTANCES FROM OSRM")
    print("=" * 80)
    print()

    # Calculate distances from both shops to all sites
    shops = [loc for loc in locations if "Shop" in loc[0]]
    sites = [loc for loc in locations if "Shop" not in loc[0]]

    print("DISTANCES FROM SHOPS TO ALL SITES:")
    print("-" * 80)
    print(f"{'Site':<25} {'From Regina':>15} {'From Saskatoon':>15} {'Closer Shop':>15}")
    print("-" * 80)

    shop_distances = {}

    for site in sites:
        site_name = site[0]
        shop_distances[site_name] = {}

        for shop in shops:
            shop_name = shop[0]
            dist, dur = get_osrm_distance(shop[1], shop[2], site[1], site[2])
            shop_distances[site_name][shop_name] = (dist, dur)
            time.sleep(0.2)  # Rate limiting

        regina_dist = shop_distances[site_name]["Regina Shop"][0] or 9999
        saskatoon_dist = shop_distances[site_name]["Saskatoon Shop"][0] or 9999
        closer = "Regina" if regina_dist < saskatoon_dist else "Saskatoon"
        diff = abs(regina_dist - saskatoon_dist)

        print(f"{site_name:<25} {regina_dist:>12.1f} km {saskatoon_dist:>12.1f} km {closer:>12} (+{diff:.0f}km)")

    print()
    print("=" * 80)
    print("INTER-SITE DISTANCES (for route optimization)")
    print("=" * 80)
    print()

    # Now calculate key inter-site distances for route planning
    # Group sites by region for focused calculations

    regina_region = ["Lumsden", "Regina ReStore", "Regina Butterfield", "Bulyea", "Strasbourg",
                     "Nokomis", "Jansen", "Moose Jaw", "Swift Current", "Yorkton"]

    saskatoon_region = ["Saskatoon LINN", "Saskatoon Old TT", "Casa Rio", "Neuanlage", "Waldheim",
                        "Rosthern", "Clavet", "Cardinal Estates", "Shields", "Allan", "Young",
                        "Prud'Homme", "Humboldt", "Tarnopol", "Prince Albert", "Nokomis", "Jansen"]

    # Key routes to verify
    route_pairs = [
        # Regina Local
        ("Regina Shop", "Lumsden"),
        ("Lumsden", "Regina ReStore"),
        ("Regina ReStore", "Regina Butterfield"),
        ("Regina Butterfield", "Regina Shop"),

        # Regina North
        ("Regina Shop", "Bulyea"),
        ("Bulyea", "Strasbourg"),
        ("Strasbourg", "Regina Shop"),

        # Regina -> Nokomis/Jansen (vs from Saskatoon)
        ("Regina Shop", "Nokomis"),
        ("Regina Shop", "Jansen"),
        ("Saskatoon Shop", "Nokomis"),
        ("Saskatoon Shop", "Jansen"),
        ("Jansen", "Nokomis"),
        ("Nokomis", "Saskatoon Shop"),

        # Regina West
        ("Regina Shop", "Moose Jaw"),
        ("Moose Jaw", "Swift Current"),
        ("Swift Current", "Regina Shop"),

        # Yorkton
        ("Regina Shop", "Yorkton"),

        # Saskatoon Local
        ("Saskatoon Shop", "Saskatoon LINN"),
        ("Saskatoon LINN", "Saskatoon Old TT"),
        ("Saskatoon Old TT", "Casa Rio"),
        ("Casa Rio", "Saskatoon Shop"),

        # Saskatoon South
        ("Saskatoon Shop", "Clavet"),
        ("Clavet", "Cardinal Estates"),
        ("Cardinal Estates", "Shields"),
        ("Shields", "Saskatoon Shop"),

        # Saskatoon East
        ("Saskatoon Shop", "Allan"),
        ("Allan", "Young"),
        ("Young", "Saskatoon Shop"),

        # Saskatoon NE
        ("Saskatoon Shop", "Prud'Homme"),
        ("Prud'Homme", "Humboldt"),
        ("Humboldt", "Tarnopol"),
        ("Tarnopol", "Saskatoon Shop"),

        # Saskatoon North
        ("Saskatoon Shop", "Neuanlage"),
        ("Neuanlage", "Waldheim"),
        ("Waldheim", "Rosthern"),
        ("Rosthern", "Saskatoon Shop"),
        ("Saskatoon Shop", "Prince Albert"),
    ]

    print(f"{'From':<25} {'To':<25} {'Distance':>12} {'Drive Time':>12}")
    print("-" * 80)

    inter_site = {}

    for from_name, to_name in route_pairs:
        from_loc = next((l for l in locations if l[0] == from_name), None)
        to_loc = next((l for l in locations if l[0] == to_name), None)

        if from_loc and to_loc:
            dist, dur = get_osrm_distance(from_loc[1], from_loc[2], to_loc[1], to_loc[2])
            inter_site[(from_name, to_name)] = (dist, dur)

            if dist:
                dur_str = f"{dur:.2f}h" if dur else "N/A"
                print(f"{from_name:<25} {to_name:<25} {dist:>9.1f} km {dur_str:>12}")
            else:
                print(f"{from_name:<25} {to_name:<25} {'ERROR':>12} {'N/A':>12}")

            time.sleep(0.2)  # Rate limiting

    return shop_distances, inter_site

def analyze_routes(shop_distances, inter_site):
    """Analyze optimal routes based on verified distances"""

    print()
    print("=" * 80)
    print("ROUTE ANALYSIS WITH VERIFIED DISTANCES")
    print("=" * 80)
    print()

    SITE_VISIT_HOURS = 2.5
    HOURLY_RATE = 85
    MILEAGE_RATE = 1.25

    def calc_route(name, branch, sites_in_order, use_inter_site=True):
        """Calculate route metrics"""
        shop = f"{branch} Shop"
        total_km = 0
        total_drive_hours = 0

        # Shop to first site
        key = (shop, sites_in_order[0])
        if key in inter_site and inter_site[key][0]:
            total_km += inter_site[key][0]
            total_drive_hours += inter_site[key][1]

        # Between sites
        for i in range(len(sites_in_order) - 1):
            key = (sites_in_order[i], sites_in_order[i+1])
            if key in inter_site and inter_site[key][0]:
                total_km += inter_site[key][0]
                total_drive_hours += inter_site[key][1]

        # Last site back to shop
        key = (sites_in_order[-1], shop)
        rev_key = (shop, sites_in_order[-1])  # Try reverse if not found
        if key in inter_site and inter_site[key][0]:
            total_km += inter_site[key][0]
            total_drive_hours += inter_site[key][1]
        elif rev_key in inter_site and inter_site[rev_key][0]:
            total_km += inter_site[rev_key][0]
            total_drive_hours += inter_site[rev_key][1]

        site_hours = len(sites_in_order) * SITE_VISIT_HOURS
        total_hours = total_drive_hours + site_hours
        overtime = max(0, total_hours - 8)

        labour_cost = (min(total_hours, 8) * HOURLY_RATE) + (overtime * HOURLY_RATE * 1.5)
        mileage_cost = total_km * MILEAGE_RATE
        total_cost = labour_cost + mileage_cost

        return {
            "name": name,
            "branch": branch,
            "sites": sites_in_order,
            "km": total_km,
            "drive_hours": total_drive_hours,
            "site_hours": site_hours,
            "total_hours": total_hours,
            "overtime": overtime,
            "labour_cost": labour_cost,
            "mileage_cost": mileage_cost,
            "total_cost": total_cost
        }

    # Define routes
    routes = [
        ("Regina Local", "Regina", ["Lumsden", "Regina ReStore", "Regina Butterfield"]),
        ("Regina North", "Regina", ["Bulyea", "Strasbourg"]),
        ("Yorkton", "Regina", ["Yorkton"]),
        ("Regina West - MJ Only", "Regina", ["Moose Jaw"]),
        ("Regina West - SC Only", "Regina", ["Swift Current"]),
        ("Regina West - Both", "Regina", ["Moose Jaw", "Swift Current"]),

        # Compare Jansen/Nokomis from both branches
        ("Jansen+Nokomis from Regina", "Regina", ["Jansen", "Nokomis"]),
        ("Jansen+Nokomis from Saskatoon", "Saskatoon", ["Jansen", "Nokomis"]),

        ("Saskatoon Local", "Saskatoon", ["Saskatoon LINN", "Saskatoon Old TT", "Casa Rio"]),
        ("Saskatoon South", "Saskatoon", ["Clavet", "Cardinal Estates", "Shields"]),
        ("Saskatoon East", "Saskatoon", ["Allan", "Young"]),
        ("Saskatoon NE", "Saskatoon", ["Prud'Homme", "Humboldt", "Tarnopol"]),
        ("Saskatoon North", "Saskatoon", ["Neuanlage", "Waldheim", "Rosthern"]),
        ("Prince Albert", "Saskatoon", ["Prince Albert"]),
    ]

    print(f"{'Route':<35} {'Sites':>5} {'km':>8} {'Drive':>7} {'Total':>7} {'OT':>5} {'Cost':>10}")
    print("-" * 90)

    results = []
    for name, branch, sites in routes:
        r = calc_route(name, branch, sites)
        results.append(r)

        status = "OK" if r["total_hours"] <= 12 else "OVER!"
        ot_str = f"{r['overtime']:.1f}h" if r["overtime"] > 0 else "-"

        print(f"{name:<35} {len(sites):>5} {r['km']:>7.0f} {r['drive_hours']:>6.1f}h {r['total_hours']:>6.1f}h {ot_str:>5} ${r['total_cost']:>8.2f}  {status}")

    print()
    print("KEY FINDINGS:")
    print("-" * 80)

    # Compare Jansen/Nokomis options
    jn_regina = next((r for r in results if "from Regina" in r["name"]), None)
    jn_saskatoon = next((r for r in results if "from Saskatoon" in r["name"]), None)

    if jn_regina and jn_saskatoon:
        print(f"\nJansen + Nokomis comparison:")
        print(f"  From Regina:    {jn_regina['km']:.0f} km, {jn_regina['total_hours']:.1f}h, ${jn_regina['total_cost']:.2f}")
        print(f"  From Saskatoon: {jn_saskatoon['km']:.0f} km, {jn_saskatoon['total_hours']:.1f}h, ${jn_saskatoon['total_cost']:.2f}")

        if jn_saskatoon['total_cost'] < jn_regina['total_cost']:
            savings = jn_regina['total_cost'] - jn_saskatoon['total_cost']
            print(f"  → Saskatoon is better by ${savings:.2f}")
        else:
            savings = jn_saskatoon['total_cost'] - jn_regina['total_cost']
            print(f"  → Regina is better by ${savings:.2f}")

    return results

if __name__ == "__main__":
    print("Starting distance verification...")
    print("This will query OSRM for actual driving distances.")
    print("Please wait, this takes about 1-2 minutes...")
    print()

    shop_distances, inter_site = build_distance_matrix()
    results = analyze_routes(shop_distances, inter_site)

    print()
    print("=" * 80)
    print("DISTANCE VERIFICATION COMPLETE")
    print("=" * 80)
