#!/usr/bin/env python3
"""Small Afdal API examples, ending with a complete dispatch workflow.

No third-party Python package is required.

    export AFDAL_API_KEY='your-key'
    python3 unified_api_examples.py modes
    python3 unified_api_examples.py route
    python3 unified_api_examples.py optimize
    python3 unified_api_examples.py full --output afdal_demo_routes.geojson
"""

import argparse
import json
import os
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen


BASE_URL = os.environ.get("AFDAL_API_URL", "https://api.afdal.tech").rstrip("/")
API_KEY = os.environ.get("AFDAL_API_KEY", "")
PHONE = "+33783719427"  # Replace with each customer's real number.


def call_api(method, path, payload=None, params=None):
    """One HTTP helper for the whole API; errors preserve status and details."""
    if path != "/api/v2/modes" and not API_KEY:
        raise SystemExit("Set AFDAL_API_KEY before calling a protected endpoint.")
    url = BASE_URL + path
    if params:
        url += "?" + urlencode(params)
    body = json.dumps(payload).encode("utf-8") if payload is not None else None
    headers = {"Accept": "application/json"}
    if API_KEY:
        headers["X-API-Key"] = API_KEY
    if body is not None:
        headers["Content-Type"] = "application/json"
    request = Request(url, data=body, headers=headers, method=method)
    try:
        with urlopen(request, timeout=180) as response:
            return json.load(response)
    except HTTPError as error:
        detail = error.read().decode("utf-8", errors="replace")
        raise SystemExit(f"HTTP {error.code} from {path}: {detail}") from error
    except URLError as error:
        raise SystemExit(f"Cannot reach {BASE_URL}: {error.reason}") from error


def example_modes():
    """1. Learn which travel modes each operation supports."""
    modes = call_api("GET", "/api/v2/modes")
    print("Routing modes:", ", ".join(modes["routing"]))
    print("Optimization modes:", ", ".join(modes["optimization"]))
    return modes


def example_route(mode="drive"):
    """2. Route through ordered points; GeoJSON uses [lon, lat]."""
    points = [
        {"lat": 33.8938, "lon": 35.5018},  # Beirut depot
        {"lat": 33.8963, "lon": 35.4841},  # Hamra
        {"lat": 33.8884, "lon": 35.5198},  # Achrafieh
    ]
    result = call_api("POST", "/api/v2/route", {"mode": mode, "points": points})
    print(f"{mode} route: {result['distance_m'] / 1000:.2f} km, "
          f"{result['duration_s'] / 60:.1f} minutes, "
          f"{len(result['coordinates'])} road coordinates")
    print("Engines:", ", ".join(result["engines"]))
    return result


def example_geocode():
    """3. Search addresses or POIs, biased towards Beirut."""
    result = call_api("GET", "/api/v2/geocode", params={
        "q": "Beirut airport", "focus_lat": 33.8938, "focus_lon": 35.5018,
    })
    for place in result["results"][:3]:
        print(f"{place['name']}: {place['lat']}, {place['lon']} — {place['label']}")
    return result


def example_optimize():
    """4. Assign stops to a vehicle and receive complete driving geometry."""
    payload = {
        "mode": "drive",  # optimization deliberately does not accept walk
        "depot": {"lat": 33.8938, "lon": 35.5018},
        "stops": [
            {"id": "H1", "address": "Hamra", "lat": 33.8963, "lon": 35.4841,
             "load": 2, "phone": PHONE},
            {"id": "A1", "address": "Achrafieh", "lat": 33.8884, "lon": 35.5198,
             "load": 1, "window_start": 540, "window_end": 960, "phone": PHONE},
            {"id": "B1", "address": "Badaro", "lat": 33.8766, "lon": 35.5148,
             "load": 1, "remarks": "Use the side entrance", "phone": PHONE},
        ],
        "fleet": {"vehicles": 1, "capacity": 4},
        "planning": {
            "start_minute": 480, "end_minute": 1140,
            "service_minutes": 5, "time_limit_seconds": 10,
            "end_policy": "depot", "service_profile": "delivery",
        },
    }
    result = call_api("POST", "/api/v2/optimize", payload)
    print(f"Optimization: {result['vehicles_used']} vehicle(s), "
          f"{result['total_distance_m'] / 1000:.2f} km")
    print("Afdal route plan ready")
    for route in result["routes"]:
        labels = [stop["order_id"] or stop["kind"] for stop in route["stops"]]
        print(f"  Vehicle {route['vehicle_id']}: {' -> '.join(labels)}; "
              f"{len(route['coordinates'])} road coordinates")
    if result["unassigned_stop_ids"]:
        print("Unassigned:", ", ".join(result["unassigned_stop_ids"]))
    return result


def example_full(output):
    """5. Discover modes, search, route, optimize, and export a GeoJSON map."""
    example_modes()
    example_geocode()
    direct_route = example_route()
    plan = example_optimize()
    features = [
        {"type": "Feature", "properties": {"kind": "direct_route"},
         "geometry": direct_route["geometry"]}
    ]
    features += [
        {"type": "Feature",
         "properties": {"kind": "optimized_route", "vehicle_id": route["vehicle_id"],
                        "distance_m": route["distance_m"]},
         "geometry": route["geometry"]}
        for route in plan["routes"]
    ]
    geojson = {"type": "FeatureCollection", "features": features}
    output.write_text(json.dumps(geojson, indent=2), encoding="utf-8")
    print(f"Saved road geometries to {output.resolve()}")


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("example", choices=["modes", "route", "walk", "geocode", "optimize", "full"])
    parser.add_argument("--output", type=Path, default=Path("afdal_demo_routes.geojson"))
    args = parser.parse_args()
    if args.example == "modes":
        example_modes()
    elif args.example == "route":
        example_route()
    elif args.example == "walk":
        example_route("walk")
    elif args.example == "geocode":
        example_geocode()
    elif args.example == "optimize":
        example_optimize()
    else:
        example_full(args.output)


if __name__ == "__main__":
    main()
