#!/usr/bin/env python3
"""
Marziel AI — Maritime Intelligence Module (Enterprise)

Full port of the C backend maritime data ingestion into Python.
Real-time data from:
  - GDACS: Tropical cyclones & disaster events
  - GDELT: Geopolitical conflict news
  - NASA EONET: Climate, fire, and volcano events
  - Yahoo Finance: Brent crude, bunker prices (VLSFO/MGO), BDI, EU Carbon, freight rates
  - AIS: Live vessel tracking (via ais-proxy)
  - Static: Strategic waterways & chokepoints
"""
import json
import logging
import urllib.request
import math
from datetime import datetime

logger = logging.getLogger("maritime_intel")

_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"


def _fetch_json(url: str, timeout: int = 15) -> dict | list | None:
    """Fetch JSON from a URL with error handling."""
    try:
        req = urllib.request.Request(url, headers={
            "Accept": "application/json",
            "User-Agent": _UA,
        })
        resp = urllib.request.urlopen(req, timeout=timeout)
        return json.loads(resp.read().decode())
    except Exception as e:
        logger.warning(f"Fetch failed for {url[:80]}: {e}")
        return None


# ══════════════════════════════════════════════════════════
# STRATEGIC WATERWAYS (Baseline Intelligence)
# ══════════════════════════════════════════════════════════

STRATEGIC_WATERWAYS = [
    {"name": "Strait of Gibraltar", "lat": 35.9, "lon": -5.3, "type": "chokepoint"},
    {"name": "Suez Canal", "lat": 30.5, "lon": 32.3, "type": "canal"},
    {"name": "Strait of Hormuz", "lat": 26.5, "lon": 56.2, "type": "chokepoint"},
    {"name": "Bab el-Mandeb", "lat": 12.5, "lon": 43.3, "type": "chokepoint"},
    {"name": "Panama Canal", "lat": 9.1, "lon": -79.6, "type": "canal"},
    {"name": "Strait of Malacca", "lat": 4.0, "lon": 99.0, "type": "chokepoint"},
    {"name": "Cape of Good Hope", "lat": -34.4, "lon": 18.5, "type": "route"},
    {"name": "English Channel", "lat": 50.9, "lon": 1.2, "type": "chokepoint"},
    {"name": "Taiwan Strait", "lat": 24.0, "lon": 119.5, "type": "chokepoint"},
    {"name": "Strait of Dover", "lat": 51.0, "lon": 1.5, "type": "chokepoint"},
]


def get_strategic_waterways() -> dict:
    """Return static strategic waterway/chokepoint data."""
    return {
        "source": "Baseline Intelligence",
        "total": len(STRATEGIC_WATERWAYS),
        "waterways": STRATEGIC_WATERWAYS,
    }


# ══════════════════════════════════════════════════════════
# GDACS: Global Disaster Alert and Coordination System
# ══════════════════════════════════════════════════════════

GDACS_API = "https://www.gdacs.org/gdacsapi/api/events/geteventlist/MAP?eventlist=TC"


def fetch_gdacs_events(max_events: int = 15) -> dict:
    """Fetch active disaster/cyclone events from GDACS."""
    data = _fetch_json(GDACS_API)
    if not data:
        return {"source": "GDACS", "error": "API unreachable", "events": [], "total": 0}

    features = data.get("features", [])[:max_events]
    events = []
    for f in features:
        props = f.get("properties", {})
        geo = f.get("geometry", {})
        coords = geo.get("coordinates", [None, None])

        # Severity mapping (same as C backend)
        alert = props.get("alertscore", "Green")
        severity = 30
        if alert == "Orange":
            severity = 70
        elif alert == "Red":
            severity = 100

        events.append({
            "name": props.get("name", "Unknown Storm"),
            "type": props.get("eventtype", "TC"),
            "alert_level": alert,
            "severity": severity,
            "date": props.get("fromdate", ""),
            "country": props.get("country", ""),
            "lat": coords[1] if len(coords) > 1 else None,
            "lon": coords[0] if len(coords) > 0 else None,
            "description": props.get("description", ""),
            "url": (props.get("url") or {}).get("report", ""),
        })

    return {
        "source": "GDACS",
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "total": len(events),
        "events": events,
    }


# ══════════════════════════════════════════════════════════
# GDELT: Global Database of Events, Language, and Tone
# ══════════════════════════════════════════════════════════

GDELT_DOC_API = (
    "https://api.gdeltproject.org/api/v2/doc/doc"
    "?query=(conflict%20OR%20tension%20OR%20war%20OR%20military"
    "%20OR%20strike%20OR%20blockade%20OR%20maritime)%20sourcelang:english"
    "&mode=ArtList&maxrecords=50&format=json"
)


def fetch_gdelt_news(query: str = "", max_articles: int = 15) -> dict:
    """Fetch geopolitical/maritime conflict news from GDELT DOC API."""
    if query:
        q = urllib.request.quote(query)
        url = (
            f"https://api.gdeltproject.org/api/v2/doc/doc"
            f"?query={q}%20sourcelang:english"
            f"&mode=ArtList&maxrecords={max_articles}&format=json&sort=datedesc"
        )
    else:
        url = GDELT_DOC_API

    data = _fetch_json(url)
    if not data:
        return {"source": "GDELT", "error": "API unreachable", "articles": [], "total": 0}

    articles = data.get("articles", [])[:max_articles]
    results = []
    for a in articles:
        results.append({
            "title": a.get("title", ""),
            "url": a.get("url", ""),
            "source": a.get("domain", ""),
            "country": a.get("sourcecountry", ""),
            "date": a.get("seendate", ""),
            "language": a.get("language", ""),
            "tone": a.get("tone", 0),
        })

    return {
        "source": "GDELT",
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "query": query or "conflict/military/maritime (default)",
        "total": len(results),
        "articles": results,
    }


# ══════════════════════════════════════════════════════════
# NASA EONET: Earth Observatory Natural Event Tracker
# ══════════════════════════════════════════════════════════

NASA_EONET_API = "https://eonet.gsfc.nasa.gov/api/v3/events?status=open&days=20"


def fetch_nasa_events(max_events: int = 20) -> dict:
    """Fetch active natural events from NASA EONET (fires, cyclones, volcanoes)."""
    data = _fetch_json(NASA_EONET_API)
    if not data:
        return {"source": "NASA EONET", "error": "API unreachable", "events": [], "total": 0}

    raw_events = data.get("events", [])[:max_events]
    events = []
    for e in raw_events:
        title = e.get("title", "")
        categories = e.get("categories", [])
        cat_title = categories[0].get("title", "Unknown") if categories else "Unknown"

        # Determine event type (same logic as C backend)
        event_type = "climate"
        if "Wildfires" in cat_title:
            event_type = "fire"
        elif "Volcanoes" in cat_title:
            event_type = "volcano"
        elif "Severe Storms" in cat_title or "Tropical" in cat_title:
            event_type = "storm"

        # Get first geometry point
        geom = e.get("geometry", [])
        lat, lon = None, None
        if geom:
            coords = geom[0].get("coordinates", [])
            if len(coords) >= 2:
                lon, lat = coords[0], coords[1]

        events.append({
            "title": title,
            "type": event_type,
            "category": cat_title,
            "lat": lat,
            "lon": lon,
            "date": geom[0].get("date", "") if geom else "",
        })

    return {
        "source": "NASA EONET",
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "total": len(events),
        "events": events,
    }


# ══════════════════════════════════════════════════════════
# YAHOO FINANCE: Market Data (Brent, BDI, Carbon, Freight)
# ══════════════════════════════════════════════════════════

def _yahoo_price(symbol: str) -> tuple:
    """Fetch current price + previous close from Yahoo Finance chart API."""
    url = f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}?interval=1d&range=5d"
    data = _fetch_json(url, timeout=10)
    if not data:
        return None, None

    try:
        result = data["chart"]["result"][0]["meta"]
        price = result.get("regularMarketPrice")
        prev = result.get("chartPreviousClose")
        return price, prev
    except (KeyError, IndexError, TypeError):
        return None, None


def fetch_market_data() -> dict:
    """Fetch live maritime market data — same sources as C backend."""
    ts = datetime.utcnow().isoformat() + "Z"
    market = {"source": "Yahoo Finance", "timestamp": ts, "data": {}}

    # ── 1. Brent Crude ──
    brent, brent_prev = _yahoo_price("BZ=F")
    if not brent or brent <= 0:
        # Fallback: WTI + $3.5
        wti, _ = _yahoo_price("CL=F")
        if wti and wti > 0:
            brent = wti + 3.5
            brent_prev = brent - 0.5

    if brent and brent > 0:
        brent_chg = ((brent - brent_prev) / brent_prev * 100) if brent_prev and brent_prev > 0 else 0
        market["data"]["brent_crude"] = {
            "price": round(brent, 2),
            "currency": "USD",
            "unit": "barrel",
            "change_pct": round(brent_chg, 2),
        }

        # ── Bunker price derivation (industry standard formulas from C backend) ──
        ports = {
            "Singapore": {"lat": 1.3, "lon": 103.8, "vlsfo_mult": 8.35, "mgo_mult": 12.20},
            "Rotterdam": {"lat": 51.9, "lon": 4.5, "vlsfo_mult": 8.65, "mgo_mult": 12.55},
            "Fujairah": {"lat": 25.1, "lon": 56.3, "vlsfo_mult": 8.20, "mgo_mult": 12.05},
            "Houston": {"lat": 29.8, "lon": -95.4, "vlsfo_mult": 8.85, "mgo_mult": 12.75},
            "Shanghai": {"lat": 31.2, "lon": 121.5, "vlsfo_mult": 8.40, "mgo_mult": 12.25},
            "Piraeus": {"lat": 37.9, "lon": 23.6, "vlsfo_mult": 8.50, "mgo_mult": 12.40},
            "Durban": {"lat": -29.9, "lon": 31.0, "vlsfo_mult": 8.55, "mgo_mult": 12.45},
            "Istanbul": {"lat": 41.0, "lon": 29.0, "vlsfo_mult": 8.48, "mgo_mult": 12.38},
        }
        bunker = {}
        for port_name, p in ports.items():
            vlsfo = round(brent * p["vlsfo_mult"], 2)
            mgo = round(brent * p["mgo_mult"], 2)
            bunker[port_name] = {"vlsfo_usd_mt": vlsfo, "mgo_usd_mt": mgo}
        market["data"]["bunker_prices"] = bunker

    # ── 2. Baltic Dry Index via BDRY ETF ──
    bdry, bdry_prev = _yahoo_price("BDRY")
    if bdry and bdry > 0:
        bdi_est = round(bdry * 200, 0)
        bdry_chg = ((bdry - bdry_prev) / bdry_prev * 100) if bdry_prev and bdry_prev > 0 else 0
        market["data"]["baltic_dry_index"] = {
            "value": bdi_est,
            "bdry_price": round(bdry, 2),
            "change_pct": round(bdry_chg, 2),
            "note": "Estimated from BDRY ETF × 200",
        }

    # ── 3. EU Carbon Price via KRBN ETF ──
    krbn, krbn_prev = _yahoo_price("KRBN")
    if krbn and krbn > 0:
        eur_usd = 1.08  # reasonable default
        eua_eur = round(krbn * 2.1 / eur_usd, 2)
        krbn_chg = ((krbn - krbn_prev) / krbn_prev * 100) if krbn_prev and krbn_prev > 0 else 0
        market["data"]["eu_carbon_eua"] = {
            "price_eur": eua_eur,
            "krbn_price": round(krbn, 2),
            "change_pct": round(krbn_chg, 2),
            "unit": "EUR/tonne CO2",
            "note": "Estimated from KRBN ETF × 2.1 / EUR_USD",
        }

    # ── 4. Freight Rate Proxies ──
    freight_symbols = [
        {"symbol": "DSX", "name": "Diana Shipping", "route": "Capesize TC", "scale": 1850},
        {"symbol": "GOGL", "name": "Golden Ocean", "route": "Panamax TC", "scale": 1520},
        {"symbol": "GNK", "name": "Genco Shipping", "route": "Supramax TC", "scale": 1280},
    ]
    freight_rates = []
    for f in freight_symbols:
        price, prev = _yahoo_price(f["symbol"])
        if price and price > 0:
            rate = round(price * f["scale"], 0)
            chg = ((price - prev) / prev * 100) if prev and prev > 0 else 0
            freight_rates.append({
                "route": f["route"],
                "rate_usd_day": rate,
                "stock": f["symbol"],
                "stock_price": round(price, 2),
                "company": f["name"],
                "change_pct": round(chg, 2),
            })
    if freight_rates:
        market["data"]["freight_rates"] = freight_rates

    return market


# ══════════════════════════════════════════════════════════
# AIS: Live Vessel Tracking (via ais-proxy)
# ══════════════════════════════════════════════════════════

AIS_PROXY_URL = "http://localhost:3001/vessels"


def fetch_ais_vessels(max_vessels: int = 25) -> dict:
    """Fetch live AIS vessel positions from the ais-proxy service."""
    data = _fetch_json(AIS_PROXY_URL, timeout=10)
    if not data:
        return {"source": "AISStream", "error": "AIS proxy unreachable", "vessels": [], "total": 0}

    vessels = []
    vessel_list = data if isinstance(data, list) else data.get("vessels", [])
    for v in vessel_list[:max_vessels]:
        vessels.append({
            "mmsi": v.get("mmsi", ""),
            "name": v.get("name", v.get("shipName", "")),
            "type": v.get("shipType", v.get("type", "")),
            "lat": v.get("lat", v.get("latitude", None)),
            "lon": v.get("lon", v.get("longitude", None)),
            "speed_knots": v.get("speed", v.get("sog", None)),
            "heading": v.get("heading", v.get("cog", None)),
            "destination": v.get("destination", ""),
            "last_update": v.get("lastUpdate", v.get("timestamp", "")),
        })

    return {
        "source": "AISStream",
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "total": len(vessels),
        "vessels": vessels,
    }


# ══════════════════════════════════════════════════════════
# COMBINED OVERVIEW
# ══════════════════════════════════════════════════════════

def fetch_maritime_overview() -> dict:
    """Full maritime intelligence overview — all sources combined."""
    gdacs = fetch_gdacs_events(max_events=10)
    gdelt = fetch_gdelt_news(max_articles=10)
    nasa = fetch_nasa_events(max_events=10)
    market = fetch_market_data()
    ais = fetch_ais_vessels(max_vessels=15)
    waterways = get_strategic_waterways()

    return {
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "disasters": gdacs,
        "geopolitical_news": gdelt,
        "natural_events": nasa,
        "market_data": market,
        "vessel_traffic": ais,
        "strategic_waterways": waterways,
    }


# ══════════════════════════════════════════════════════════
# FORMAT FOR LLM CONTEXT
# ══════════════════════════════════════════════════════════

def format_maritime_for_llm(data: dict) -> str:
    """Format the full maritime intelligence into readable text for the LLM."""
    lines = [f"📡 MARITIME INTELLIGENCE REPORT ({data.get('timestamp', 'now')})"]
    lines.append("=" * 60)

    # Disasters (GDACS)
    disasters = data.get("disasters", {})
    if disasters.get("events"):
        lines.append(f"\n🌊 ACTIVE DISASTERS — GDACS ({disasters['total']} events):")
        for e in disasters["events"]:
            sev = f"[{e.get('alert_level', '?')}]" if e.get("alert_level") else ""
            lines.append(f"  • {e['name']} ({e.get('type', '')}) {sev}")
            if e.get("country"):
                lines.append(f"    Country: {e['country']}")
            if e.get("lat") and e.get("lon"):
                lines.append(f"    Position: {e['lat']:.2f}°N, {e['lon']:.2f}°E")

    # Geopolitical news (GDELT)
    news = data.get("geopolitical_news", {})
    if news.get("articles"):
        lines.append(f"\n📰 GEOPOLITICAL NEWS — GDELT ({news['total']} articles):")
        for a in news["articles"][:10]:
            lines.append(f"  • {a['title']}")
            lines.append(f"    Source: {a['source']} ({a.get('country', '?')}) | {a['date']}")

    # Natural events (NASA EONET)
    nasa = data.get("natural_events", {})
    if nasa.get("events"):
        lines.append(f"\n🌍 NATURAL EVENTS — NASA EONET ({nasa['total']} events):")
        for e in nasa["events"][:10]:
            pos = f" @ {e['lat']:.1f}°N, {e['lon']:.1f}°E" if e.get("lat") else ""
            lines.append(f"  • [{e.get('type', '?').upper()}] {e['title']}{pos}")

    # Market data (Yahoo Finance)
    market = data.get("market_data", {}).get("data", {})
    if market:
        lines.append(f"\n💰 MARITIME MARKET DATA:")
        if "brent_crude" in market:
            b = market["brent_crude"]
            lines.append(f"  • Brent Crude: ${b['price']}/bbl ({b['change_pct']:+.2f}%)")
        if "baltic_dry_index" in market:
            bdi = market["baltic_dry_index"]
            lines.append(f"  • Baltic Dry Index: {bdi['value']:.0f} ({bdi['change_pct']:+.2f}%)")
        if "eu_carbon_eua" in market:
            eua = market["eu_carbon_eua"]
            lines.append(f"  • EU Carbon (EUA): €{eua['price_eur']}/tonne ({eua['change_pct']:+.2f}%)")
        if "bunker_prices" in market:
            lines.append(f"  • Bunker Prices (VLSFO / MGO per MT):")
            for port, p in market["bunker_prices"].items():
                lines.append(f"      {port}: ${p['vlsfo_usd_mt']} / ${p['mgo_usd_mt']}")
        if "freight_rates" in market:
            lines.append(f"  • Freight Rates:")
            for f in market["freight_rates"]:
                lines.append(f"      {f['route']}: ${f['rate_usd_day']:.0f}/day ({f['stock']} ${f['stock_price']})")

    # AIS
    ais = data.get("vessel_traffic", {})
    if ais.get("vessels"):
        lines.append(f"\n🚢 LIVE VESSEL TRAFFIC — AIS ({ais['total']} vessels):")
        for v in ais["vessels"][:10]:
            name = v.get("name") or f"MMSI:{v.get('mmsi', '?')}"
            speed = f"{v['speed_knots']:.1f}kn" if v.get("speed_knots") else "N/A"
            lines.append(f"  • {name} — {v.get('type', '?')} | {speed}")
            if v.get("destination"):
                lines.append(f"    → {v['destination']}")

    # Strategic waterways
    ww = data.get("strategic_waterways", {})
    if ww.get("waterways"):
        lines.append(f"\n⚓ STRATEGIC WATERWAYS ({ww['total']} chokepoints):")
        for w in ww["waterways"]:
            lines.append(f"  • {w['name']} ({w['type']}) — {w['lat']:.1f}°N, {w['lon']:.1f}°E")

    if not any([
        disasters.get("events"), news.get("articles"), nasa.get("events"),
        market, ais.get("vessels"),
    ]):
        lines.append("\n⚠️ No real-time data available at this time.")

    return "\n".join(lines)
