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

LLM-powered analytical reports. Feeds raw maritime data to the local LLM
with domain-expert prompts. Produces insights, risk assessments, and
actionable recommendations — not just data dumps.

Report types:
  1. Situational Analysis — global risk correlation
  2. Engine Report — performance & maintenance prediction
  3. Fuel Report — consumption, cost, bunkering strategy
  4. Weather/Wave Report — route conditions & timing
  5. Route Report — risk zones, piracy, alternatives
  6. Carbon Emission Report — CO₂, CII rating, EU ETS cost
"""
import json
import logging
import os
import urllib.request
from datetime import datetime

logger = logging.getLogger("maritime_analyst")


# ══════════════════════════════════════════════════════════
# LLM INFERENCE HELPER
# ══════════════════════════════════════════════════════════

def _llm_analyze(system_prompt: str, data_context: str, max_tokens: int = 2048) -> str:
    """Send data + prompt to the local LLM and return the analysis."""
    llm_url = os.environ.get("VLLM_URL", "http://localhost:8000")

    # Detect model
    try:
        req = urllib.request.Request(f"{llm_url}/v1/models")
        resp = urllib.request.urlopen(req, timeout=5)
        models = json.loads(resp.read().decode())
        model_id = models["data"][0]["id"] if models.get("data") else ""
    except Exception:
        model_id = ""

    if not model_id:
        return "[LLM unavailable — raw data included below]\n\n" + data_context

    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": data_context},
    ]

    try:
        payload = json.dumps({
            "model": model_id,
            "messages": messages,
            "temperature": 0.3,
            "max_tokens": max_tokens,
        }).encode()

        req = urllib.request.Request(
            f"{llm_url}/v1/chat/completions",
            data=payload,
            headers={"Content-Type": "application/json"},
        )
        resp = urllib.request.urlopen(req, timeout=120)
        result = json.loads(resp.read().decode())
        return result["choices"][0]["message"]["content"]
    except Exception as e:
        logger.error(f"LLM analysis failed: {e}")
        return f"[LLM analysis failed: {e}]\n\nRaw data:\n{data_context}"


# ══════════════════════════════════════════════════════════
# 1. SITUATIONAL ANALYSIS
# ══════════════════════════════════════════════════════════

_SITUATION_PROMPT = """You are a senior maritime intelligence analyst at a major shipping company.
Analyze the following real-time maritime data and produce a STRATEGIC INTELLIGENCE BRIEFING.

Your analysis MUST include:
1. THREAT ASSESSMENT — Rate overall maritime risk (CRITICAL/HIGH/MODERATE/LOW) with justification
2. TRADE ROUTE IMPACT — Which major shipping lanes are affected by current disasters/conflicts?
3. MARKET CORRELATION — Connect market movements (oil, freight, carbon) to geopolitical events
4. OPERATIONAL RECOMMENDATIONS — Actionable advice for fleet operators
5. 72-HOUR OUTLOOK — What should operators prepare for?

Be specific. Name routes, straits, ports. Use the actual data provided.
Write in professional intelligence briefing style — concise, analytical, decisive."""


def analyze_maritime_situation() -> dict:
    """LLM-powered global maritime situational analysis."""
    from marziel.maritime_intel import (
        fetch_gdacs_events, fetch_gdelt_news, fetch_nasa_events,
        fetch_market_data, fetch_ais_vessels,
    )
    from marziel.maritime_services import (
        get_risk_zones, fetch_exchangerate_data,
    )

    ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")

    # Collect all data
    gdacs = fetch_gdacs_events(max_events=15)
    nasa = fetch_nasa_events(max_events=10)
    gdelt = fetch_gdelt_news(max_articles=10)
    market = fetch_market_data()
    fx = fetch_exchangerate_data()
    risk = get_risk_zones()
    ais = fetch_ais_vessels(max_vessels=10)

    # Compress into context
    ctx = [f"MARITIME INTELLIGENCE DATA — {ts}\n"]

    ctx.append("=== ACTIVE DISASTERS (GDACS) ===")
    for e in gdacs.get("events", [])[:10]:
        ctx.append(f"- {e.get('name','?')} | Type: {e.get('type','?')} | Severity: {e.get('alert_level','?')} | Location: ({e.get('lat','?')}°, {e.get('lon','?')}°) | Country: {e.get('country','?')}")

    ctx.append("\n=== NATURAL EVENTS (NASA) ===")
    for e in nasa.get("events", [])[:8]:
        ctx.append(f"- {e.get('title','?')} | Type: {e.get('type','?')} | Location: ({e.get('lat','?')}°, {e.get('lon','?')}°)")

    ctx.append("\n=== GEOPOLITICAL NEWS (GDELT) ===")
    for a in gdelt.get("articles", [])[:8]:
        ctx.append(f"- {a.get('title','?')} | Source: {a.get('source','?')} | Country: {a.get('country','?')}")

    ctx.append("\n=== MARKET DATA ===")
    md = market.get("data", {})
    if "brent_crude" in md:
        b = md["brent_crude"]
        ctx.append(f"- Brent Crude: ${b['price']}/bbl ({b['change_pct']:+.2f}%)")
    if "baltic_dry_index" in md:
        bdi = md["baltic_dry_index"]
        ctx.append(f"- Baltic Dry Index: {bdi['value']:.0f} ({bdi['change_pct']:+.2f}%)")
    if "eu_carbon_eua" in md:
        eua = md["eu_carbon_eua"]
        ctx.append(f"- EU Carbon (EUA): €{eua['price_eur']}/tonne ({eua['change_pct']:+.2f}%)")
    if "freight_rates" in md:
        for f in md["freight_rates"]:
            ctx.append(f"- Freight: {f['route']} ${f['rate_usd_day']:.0f}/day ({f['change_pct']:+.1f}%)")
    if "bunker_prices" in md:
        for port, p in list(md["bunker_prices"].items())[:5]:
            ctx.append(f"- Bunker {port}: VLSFO ${p['vlsfo_usd_mt']}/MT, MGO ${p['mgo_usd_mt']}/MT")

    ctx.append("\n=== EXCHANGE RATES (USD basis) ===")
    for ccy, rate in fx.get("rates", {}).items():
        if rate:
            ctx.append(f"- {ccy}: {rate:.4f}")

    ctx.append(f"\n=== RISK ZONES: {risk.get('total', 0)} active zones ===")
    ctx.append(f"=== AIS VESSELS: {ais.get('total', 0)} currently tracked ===")

    data_context = "\n".join(ctx)
    analysis = _llm_analyze(_SITUATION_PROMPT, data_context)

    return {
        "type": "situational_analysis",
        "timestamp": ts,
        "analysis": analysis,
        "data_sources": {
            "gdacs_events": gdacs.get("total", 0),
            "nasa_events": nasa.get("total", 0),
            "gdelt_articles": gdelt.get("total", 0),
            "risk_zones": risk.get("total", 0),
            "ais_vessels": ais.get("total", 0),
        },
    }


# ══════════════════════════════════════════════════════════
# 2. VESSEL ENGINE REPORT
# ══════════════════════════════════════════════════════════

_ENGINE_PROMPT = """You are a chief marine engineer with 20 years of experience.
Analyze the vessel data, maintenance history, and fuel records provided.

Produce a VESSEL ENGINE PERFORMANCE REPORT including:
1. ENGINE STATUS — Current operational condition assessment
2. PERFORMANCE METRICS — Fuel efficiency, operating hours analysis
3. MAINTENANCE ANALYSIS — Pattern recognition in maintenance history
4. PREDICTED MAINTENANCE — When is the next service likely due?
5. ANOMALY DETECTION — Any concerning patterns in fuel consumption or maintenance?
6. RECOMMENDATIONS — Specific actionable items for the engineering team

Use maritime engineering terminology. Be specific about component names and metrics."""


def generate_engine_report(vessel_id: int = None) -> dict:
    """LLM-powered vessel engine performance analysis."""
    from marziel.maritime_services import (
        get_fleet_vessels, get_vessel_detail, get_maintenance, get_fuel_records,
    )

    ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")

    # Collect vessel data
    if vessel_id:
        vessel = get_vessel_detail(vessel_id)
        maintenance = get_maintenance(vessel_id)
    else:
        fleet = get_fleet_vessels()
        vessel = {"fleet_summary": f"{fleet.get('total', 0)} vessels", "vessels": fleet.get("vessels", [])[:5]}
        maintenance = get_maintenance()

    fuel = get_fuel_records()

    ctx = [f"VESSEL ENGINE DATA — {ts}\n"]
    ctx.append(f"=== VESSEL INFO ===\n{json.dumps(vessel, indent=2, default=str)[:2000]}")
    ctx.append(f"\n=== MAINTENANCE RECORDS ({maintenance.get('total', 0)} records) ===\n{json.dumps(maintenance.get('records', [])[:20], indent=2, default=str)[:2000]}")
    ctx.append(f"\n=== FUEL RECORDS ({fuel.get('total', 0)} records) ===\n{json.dumps(fuel.get('records', [])[:20], indent=2, default=str)[:2000]}")

    data_context = "\n".join(ctx)
    analysis = _llm_analyze(_ENGINE_PROMPT, data_context)

    return {
        "type": "engine_report",
        "vessel_id": vessel_id,
        "timestamp": ts,
        "analysis": analysis,
        "data_sources": {
            "maintenance_records": maintenance.get("total", 0),
            "fuel_records": fuel.get("total", 0),
        },
    }


# ══════════════════════════════════════════════════════════
# 3. FUEL CONSUMPTION REPORT
# ══════════════════════════════════════════════════════════

_FUEL_PROMPT = """You are a maritime fuel optimization specialist.
Analyze the fuel consumption data, bunker prices, and voyage information provided.

Produce a FUEL CONSUMPTION & COST ANALYSIS including:
1. CONSUMPTION OVERVIEW — Total fuel burned, daily averages, trends
2. COST ANALYSIS — Total fuel cost using current bunker port prices
3. EFFICIENCY ASSESSMENT — Actual consumption vs. industry benchmarks
4. BUNKERING STRATEGY — Cheapest ports for next refueling based on live prices
5. SPEED-FUEL OPTIMIZATION — Recommend optimal speed for fuel savings
6. SAVINGS POTENTIAL — Quantify potential cost savings with recommendations

Include specific numbers. Compare to IMO guidelines where relevant."""


def generate_fuel_report(vessel_id: int = None) -> dict:
    """LLM-powered fuel consumption and cost analysis."""
    from marziel.maritime_intel import fetch_market_data
    from marziel.maritime_services import (
        get_fuel_records, get_voyages, fetch_shipandbunker_estimate,
    )

    ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")

    fuel = get_fuel_records()
    voyages = get_voyages(vessel_id)
    market = fetch_market_data()
    brent = market.get("data", {}).get("brent_crude", {}).get("price", 0)
    bunker = fetch_shipandbunker_estimate(brent) if brent else {}

    ctx = [f"FUEL ANALYSIS DATA — {ts}\n"]
    ctx.append(f"=== FUEL RECORDS ({fuel.get('total', 0)} entries) ===\n{json.dumps(fuel.get('records', [])[:30], indent=2, default=str)[:2500]}")
    ctx.append(f"\n=== ACTIVE VOYAGES ===\n{json.dumps(voyages.get('voyages', [])[:10], indent=2, default=str)[:1500]}")
    ctx.append(f"\n=== CURRENT BUNKER PRICES (Brent ${brent}/bbl) ===")
    for port, p in bunker.get("ports", {}).items():
        ctx.append(f"  {port}: VLSFO ${p['vlsfo_usd_mt']}/MT | MGO ${p['mgo_usd_mt']}/MT | HSFO ${p['hsfo_usd_mt']}/MT")

    data_context = "\n".join(ctx)
    analysis = _llm_analyze(_FUEL_PROMPT, data_context)

    return {
        "type": "fuel_report",
        "vessel_id": vessel_id,
        "timestamp": ts,
        "analysis": analysis,
        "brent_price": brent,
        "data_sources": {
            "fuel_records": fuel.get("total", 0),
            "voyages": voyages.get("total", 0),
            "bunker_ports": len(bunker.get("ports", {})),
        },
    }


# ══════════════════════════════════════════════════════════
# 4. WEATHER & WAVE REPORT
# ══════════════════════════════════════════════════════════

_WEATHER_PROMPT = """You are a maritime meteorologist and route weather advisor.
Analyze the disaster events, natural phenomena, and environmental data provided.

Produce a MARITIME WEATHER & SEA STATE BRIEFING including:
1. CURRENT CONDITIONS — Active weather events affecting shipping lanes
2. WAVE & SEA STATE — Assess conditions along major trade routes based on active events
3. STORM TRACKING — Active cyclones, typhoons, and their projected paths near shipping lanes
4. ROUTE IMPACT ASSESSMENT — Which routes are dangerous, which are clear?
5. DEPARTURE TIMING — Optimal departure windows to avoid adverse conditions
6. CREW SAFETY ADVISORY — Risk levels for different vessel types

Reference specific coordinates and regions. Be precise about timing."""


def generate_weather_report(lat: float = None, lon: float = None,
                            origin: str = None, destination: str = None) -> dict:
    """LLM-powered weather and wave analysis for maritime routes."""
    from marziel.maritime_intel import (
        fetch_gdacs_events, fetch_nasa_events, get_strategic_waterways,
    )
    from marziel.maritime_services import get_risk_zones

    ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")

    gdacs = fetch_gdacs_events(max_events=15)
    nasa = fetch_nasa_events(max_events=15)
    risk = get_risk_zones()
    waterways = get_strategic_waterways()

    ctx = [f"MARITIME WEATHER DATA — {ts}\n"]

    if origin and destination:
        ctx.append(f"REQUESTED ROUTE: {origin} → {destination}\n")
    if lat and lon:
        ctx.append(f"FOCUS AREA: {lat}°N, {lon}°E\n")

    ctx.append("=== ACTIVE DISASTERS (GDACS) ===")
    for e in gdacs.get("events", []):
        etype = e.get("type", "?")
        if etype.lower() in ("tc", "cyclone", "storm", "flood", "tsunami"):
            ctx.append(f"- ⚠️ {e['name']} | Type: {etype} | Severity: {e.get('alert_level','?')} | Position: ({e.get('lat','?')}°, {e.get('lon','?')}°) | Country: {e.get('country','?')}")

    ctx.append("\n=== NATURAL EVENTS (NASA EONET) ===")
    for e in nasa.get("events", []):
        ctx.append(f"- {e.get('title','?')} | Type: {e.get('type','?')} | Position: ({e.get('lat','?')}°, {e.get('lon','?')}°)")

    # Weather-related risk zones
    ctx.append(f"\n=== WEATHER RISK ZONES ({risk.get('total', 0)} total) ===")
    for z in risk.get("zones", [])[:15]:
        ztype = z.get("zone_type", z.get("type", ""))
        if ztype.lower() in ("weather", "storm", "cyclone", "flood", "climate", "tsunami"):
            ctx.append(f"- {z.get('name', '?')} | Type: {ztype} | Severity: {z.get('severity', '?')}")

    ctx.append("\n=== STRATEGIC WATERWAYS ===")
    for w in waterways.get("waterways", [])[:10]:
        ctx.append(f"- {w['name']} ({w['type']}) — {w['lat']:.1f}°N, {w['lon']:.1f}°E")

    data_context = "\n".join(ctx)
    analysis = _llm_analyze(_WEATHER_PROMPT, data_context)

    return {
        "type": "weather_report",
        "route": {"origin": origin, "destination": destination} if origin else None,
        "position": {"lat": lat, "lon": lon} if lat else None,
        "timestamp": ts,
        "analysis": analysis,
        "data_sources": {
            "gdacs_events": gdacs.get("total", 0),
            "nasa_events": nasa.get("total", 0),
            "risk_zones": risk.get("total", 0),
        },
    }


# ══════════════════════════════════════════════════════════
# 5. ROUTE RISK REPORT
# ══════════════════════════════════════════════════════════

_ROUTE_PROMPT = """You are a maritime risk and route planning expert.
Analyze the route, risk zones, geopolitical threats, and market data provided.

Produce a ROUTE RISK & OPTIMIZATION REPORT including:
1. ROUTE OVERVIEW — Primary route description with key waypoints and straits
2. RISK ASSESSMENT — Overlay active risk zones (weather, conflict, piracy) on the route
3. PIRACY & SECURITY — Specific security threats along the route (Gulf of Aden, Strait of Malacca, etc.)
4. ESTIMATED TRANSIT — Distance, days at sea at standard speed (12-14 knots)
5. FUEL COST ESTIMATE — Based on current bunker prices at departure/arrival ports
6. ALTERNATIVE ROUTES — If risks are high, suggest alternatives with trade-offs
7. OVERALL RISK RATING — Rate the route (CRITICAL/HIGH/MODERATE/LOW)

Be specific about geography. Name straits, canals, and waypoints."""


def generate_route_report(origin: str = "Rotterdam", destination: str = "Singapore") -> dict:
    """LLM-powered route risk assessment and optimization."""
    from marziel.maritime_intel import (
        fetch_gdacs_events, fetch_gdelt_news, fetch_market_data,
        get_strategic_waterways,
    )
    from marziel.maritime_services import (
        get_risk_zones, get_ports, fetch_shipandbunker_estimate,
    )

    ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")

    gdacs = fetch_gdacs_events(max_events=15)
    gdelt = fetch_gdelt_news(max_articles=8)
    risk = get_risk_zones()
    waterways = get_strategic_waterways()
    market = fetch_market_data()
    brent = market.get("data", {}).get("brent_crude", {}).get("price", 0)
    bunker = fetch_shipandbunker_estimate(brent) if brent else {}

    ctx = [f"ROUTE ANALYSIS DATA — {ts}"]
    ctx.append(f"ROUTE: {origin} → {destination}\n")

    ctx.append("=== ACTIVE RISK ZONES ===")
    for z in risk.get("zones", [])[:20]:
        ctx.append(f"- {z.get('name', '?')} | Type: {z.get('zone_type', z.get('type', '?'))} | Severity: {z.get('severity', '?')} | Position: ({z.get('lat', '?')}, {z.get('lon', '?')})")

    ctx.append("\n=== ACTIVE DISASTERS (GDACS) ===")
    for e in gdacs.get("events", [])[:10]:
        ctx.append(f"- {e.get('name','?')} | {e.get('type','?')} | ({e.get('lat','?')}°, {e.get('lon','?')}°)")

    ctx.append("\n=== GEOPOLITICAL TENSIONS ===")
    for a in gdelt.get("articles", [])[:6]:
        ctx.append(f"- {a.get('title','?')} ({a.get('country','?')})")

    ctx.append("\n=== STRATEGIC WATERWAYS ===")
    for w in waterways.get("waterways", []):
        ctx.append(f"- {w['name']} ({w['type']}) — {w['lat']:.1f}°N, {w['lon']:.1f}°E")

    ctx.append(f"\n=== BUNKER PRICES (Brent ${brent}/bbl) ===")
    for port, p in list(bunker.get("ports", {}).items())[:8]:
        ctx.append(f"- {port}: VLSFO ${p['vlsfo_usd_mt']}/MT | MGO ${p['mgo_usd_mt']}/MT")

    md = market.get("data", {})
    if "freight_rates" in md:
        ctx.append("\n=== FREIGHT RATES ===")
        for f in md["freight_rates"]:
            ctx.append(f"- {f['route']}: ${f['rate_usd_day']:.0f}/day")

    data_context = "\n".join(ctx)
    analysis = _llm_analyze(_ROUTE_PROMPT, data_context)

    return {
        "type": "route_report",
        "origin": origin,
        "destination": destination,
        "timestamp": ts,
        "analysis": analysis,
        "data_sources": {
            "risk_zones": risk.get("total", 0),
            "gdacs_events": gdacs.get("total", 0),
            "gdelt_articles": gdelt.get("total", 0),
            "bunker_ports": len(bunker.get("ports", {})),
        },
    }


# ══════════════════════════════════════════════════════════
# 6. CARBON EMISSION REPORT
# ══════════════════════════════════════════════════════════

# IMO emission factors (tonnes CO₂ per tonne of fuel)
IMO_EMISSION_FACTORS = {
    "HFO":   3.114,   # Heavy Fuel Oil
    "HSFO":  3.114,
    "VLSFO": 3.151,   # Very Low Sulphur Fuel Oil
    "MGO":   3.206,   # Marine Gas Oil
    "MDO":   3.206,   # Marine Diesel Oil
    "LNG":   2.750,   # Liquefied Natural Gas
    "LPG":   3.000,   # Liquefied Petroleum Gas
    "methanol": 1.375,
}

_CARBON_PROMPT = """You are a maritime environmental compliance officer and IMO regulations expert.
Analyze the fuel consumption data, carbon prices, and vessel information provided.

Produce a CARBON EMISSION & COMPLIANCE REPORT including:
1. EMISSION SUMMARY — Total CO₂ emissions (tonnes) based on fuel burned × IMO emission factors
2. CII RATING — Calculate the Carbon Intensity Indicator (A/B/C/D/E rating)
   - CII = (CO₂ emissions in grams) / (DWT × Distance in nautical miles)
   - A ≤ 0.85×ref, B ≤ 0.93×ref, C ≤ 1.07×ref, D ≤ 1.19×ref, E > 1.19×ref
3. EU ETS COST — Calculate EU Emissions Trading System cost (emissions × EUA carbon price)
4. IMO 2030 COMPLIANCE — Assess against IMO 2030 targets (40% reduction from 2008 baseline)
5. REDUCTION RECOMMENDATIONS — Specific actions to improve CII rating
6. CARBON CREDIT ANALYSIS — Estimated carbon credit offset cost

Use IMO emission factors: HFO=3.114, VLSFO=3.151, MGO=3.206, LNG=2.750 tCO₂/tFuel.
Be precise with calculations. Show your work."""


def generate_carbon_report(vessel_id: int = None) -> dict:
    """LLM-powered carbon emission and CII compliance analysis."""
    from marziel.maritime_intel import fetch_market_data
    from marziel.maritime_services import (
        get_fuel_records, get_fleet_vessels, get_voyages,
    )

    ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")

    fuel = get_fuel_records()
    voyages = get_voyages(vessel_id)
    market = fetch_market_data()

    # Get carbon price
    md = market.get("data", {})
    carbon_price = md.get("eu_carbon_eua", {}).get("price_eur", 0)
    brent = md.get("brent_crude", {}).get("price", 0)

    if vessel_id:
        from marziel.maritime_services import get_vessel_detail
        vessel = get_vessel_detail(vessel_id)
    else:
        fleet = get_fleet_vessels()
        vessel = {"fleet_size": fleet.get("total", 0), "vessels": fleet.get("vessels", [])[:5]}

    # Pre-calculate emissions from fuel records
    total_fuel_mt = 0
    estimated_co2 = 0
    for rec in fuel.get("records", []):
        qty = rec.get("quantity", rec.get("fuel_consumed", 0))
        ftype = rec.get("fuel_type", "VLSFO").upper()
        if qty:
            total_fuel_mt += qty
            factor = IMO_EMISSION_FACTORS.get(ftype, 3.151)
            estimated_co2 += qty * factor

    ctx = [f"CARBON EMISSION DATA — {ts}\n"]
    ctx.append(f"=== VESSEL INFO ===\n{json.dumps(vessel, indent=2, default=str)[:1500]}")
    ctx.append(f"\n=== FUEL RECORDS ({fuel.get('total', 0)} entries) ===\n{json.dumps(fuel.get('records', [])[:25], indent=2, default=str)[:2000]}")
    ctx.append(f"\n=== PRE-CALCULATED EMISSIONS ===")
    ctx.append(f"Total fuel consumed: {total_fuel_mt:.1f} MT")
    ctx.append(f"Estimated CO₂ emissions: {estimated_co2:.1f} tonnes")
    ctx.append(f"\nIMO Emission Factors: {json.dumps(IMO_EMISSION_FACTORS)}")
    ctx.append(f"\n=== CARBON MARKET ===")
    ctx.append(f"EU EUA Carbon Price: €{carbon_price}/tonne")
    ctx.append(f"Estimated EU ETS cost: €{estimated_co2 * carbon_price:,.0f}")
    ctx.append(f"Brent Crude: ${brent}/bbl")

    ctx.append(f"\n=== VOYAGES ({voyages.get('total', 0)}) ===\n{json.dumps(voyages.get('voyages', [])[:10], indent=2, default=str)[:1500]}")

    data_context = "\n".join(ctx)
    analysis = _llm_analyze(_CARBON_PROMPT, data_context)

    return {
        "type": "carbon_emission_report",
        "vessel_id": vessel_id,
        "timestamp": ts,
        "analysis": analysis,
        "calculated": {
            "total_fuel_mt": round(total_fuel_mt, 1),
            "total_co2_tonnes": round(estimated_co2, 1),
            "eu_ets_cost_eur": round(estimated_co2 * carbon_price, 2),
            "carbon_price_eur": carbon_price,
        },
        "data_sources": {
            "fuel_records": fuel.get("total", 0),
            "voyages": voyages.get("total", 0),
        },
    }
