#!/usr/bin/env python3
"""Render a Leaflet HTML map combining US data centers, submarine cables,
and city-level network-dominance points from PostGIS.
"""
import argparse
import json
import os

import psycopg2


DB_NAME = "data_centers"
DC_TABLE = "public.master_data_centers"
CABLES_TABLE = "public.internet_cables"
CITY_TABLE = "public.internet_city_dominance"


def connect():
    return psycopg2.connect(
        host=os.environ["PGWEB_HOST"],
        port=os.environ["PGWEB_PORT"],
        user=os.environ["PGWEB_USER"],
        password=os.environ["PGWEB_PASSWORD"],
        dbname=DB_NAME,
    )


def load_data_centers(conn):
    with conn.cursor() as cur:
        cur.execute(
            f"""
            select
                master_id,
                source,
                coalesce(operator, ''),
                coalesce(name, ''),
                coalesce(city, ''),
                coalesce(state, ''),
                longitude,
                latitude
            from {DC_TABLE}
            where longitude is not null and latitude is not null
            """
        )
        return [
            {
                "id": r[0],
                "source": r[1],
                "operator": r[2],
                "name": r[3],
                "city": r[4],
                "state": r[5],
                "lon": float(r[6]),
                "lat": float(r[7]),
            }
            for r in cur.fetchall()
        ]


def load_cables(conn):
    with conn.cursor() as cur:
        cur.execute(
            f"""
            select
                feature_id,
                coalesce(cable_id, ''),
                coalesce(name, ''),
                coalesce(color, '#888888'),
                coalesce(owners, ''),
                rfs_year,
                decommission_year,
                length_km,
                coalesce(url, ''),
                ST_AsGeoJSON(geom)
            from {CABLES_TABLE}
            where geom is not null
            """
        )
        features = []
        for r in cur.fetchall():
            features.append(
                {
                    "type": "Feature",
                    "geometry": json.loads(r[9]),
                    "properties": {
                        "feature_id": r[0],
                        "cable_id": r[1],
                        "name": r[2],
                        "color": r[3],
                        "owners": r[4],
                        "rfs_year": r[5],
                        "decommission_year": r[6],
                        "length_km": float(r[7]) if r[7] is not None else None,
                        "url": r[8],
                    },
                }
            )
        return {"type": "FeatureCollection", "features": features}


def load_cities(conn, us_only=False):
    where = "where geom is not null"
    if us_only:
        where += " and country = 'US'"
    with conn.cursor() as cur:
        cur.execute(
            f"""
            select
                id,
                coalesce(city, ''),
                coalesce(country, ''),
                coalesce(country_name, ''),
                coalesce(region, ''),
                physical_capacity_tbps,
                logical_dominance_ips,
                longitude,
                latitude
            from {CITY_TABLE}
            {where}
            """
        )
        return [
            {
                "id": r[0],
                "city": r[1],
                "country": r[2],
                "country_name": r[3],
                "region": r[4],
                "tbps": float(r[5]) if r[5] is not None else None,
                "ips": int(r[6]) if r[6] is not None else None,
                "lon": float(r[7]),
                "lat": float(r[8]),
            }
            for r in cur.fetchall()
        ]


def render_html(data_centers, cables_geojson, cities, output_path):
    payload = json.dumps(
        {
            "data_centers": data_centers,
            "cables": cables_geojson,
            "cities": cities,
        }
    )

    html = """<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>US Data Centers + Submarine Cables</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
  <style>
    html, body { height: 100%; margin: 0; font-family: system-ui, -apple-system, Segoe UI, sans-serif; }
    #layout { display: grid; grid-template-columns: 300px 1fr; height: 100%; }
    #panel { padding: 14px; border-right: 1px solid #ddd; overflow: auto; background: #f8fafb; font-size: 13px; }
    #map { height: 100%; width: 100%; }
    h1 { margin: 0 0 8px; font-size: 18px; }
    h2 { margin: 14px 0 6px; font-size: 13px; text-transform: uppercase; color: #555; letter-spacing: 0.04em; }
    .row { display: flex; justify-content: space-between; padding: 2px 0; }
    .swatch { width: 12px; height: 12px; display: inline-block; margin-right: 8px; vertical-align: middle; border: 1px solid #ccc; }
    label.toggle { display: block; padding: 3px 0; cursor: pointer; }
    @media (max-width: 900px) {
      #layout { grid-template-columns: 1fr; grid-template-rows: 220px 1fr; }
      #panel { border-right: 0; border-bottom: 1px solid #ddd; }
    }
  </style>
</head>
<body>
  <div id="layout">
    <div id="panel">
      <h1>Data Centers + Cables</h1>
      <div class="row"><span>Data centers</span><strong id="dcCount"></strong></div>
      <div class="row"><span>Submarine cables</span><strong id="cableCount"></strong></div>
      <div class="row"><span>City dominance pts</span><strong id="cityCount"></strong></div>

      <h2>Layers</h2>
      <label class="toggle"><input type="checkbox" id="tDc" checked> Data centers</label>
      <label class="toggle"><input type="checkbox" id="tCables" checked> Submarine cables</label>
      <label class="toggle"><input type="checkbox" id="tCities" checked> City dominance</label>

      <h2>Data center source</h2>
      <div class="row"><span><span class="swatch" style="background:#2ca02c"></span>merged (curated + OSM)</span></div>
      <div class="row"><span><span class="swatch" style="background:#1f77b4"></span>curated only</span></div>
      <div class="row"><span><span class="swatch" style="background:#ff7f0e"></span>osm only</span></div>
      <div class="row"><span><span class="swatch" style="background:#7f7f7f"></span>other</span></div>

      <h2>City dominance</h2>
      <div class="row"><span><span class="swatch" style="background:#9b59b6;border-radius:50%"></span>Sized by physical Tbps</span></div>
    </div>
    <div id="map"></div>
  </div>

  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <script>
    const DATA = __PAYLOAD__;

    function colorForSource(source) {
      if (source === 'merged') return '#2ca02c';
      if (source === 'curated') return '#1f77b4';
      if (source === 'osm') return '#ff7f0e';
      return '#7f7f7f';
    }

    function esc(v) {
      return String(v == null ? '' : v)
        .replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;')
        .replaceAll('"','&quot;').replaceAll("'", '&#39;');
    }

    const map = L.map('map', { preferCanvas: true, worldCopyJump: true }).setView([20, -40], 3);

    L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
      maxZoom: 19,
      attribution: '&copy; OpenStreetMap contributors'
    }).addTo(map);

    const cableLayer = L.geoJSON(DATA.cables, {
      style: f => ({
        color: f.properties.color || '#888',
        weight: 1.4,
        opacity: 0.75,
      }),
      onEachFeature: (feature, layer) => {
        const p = feature.properties;
        const yrs = [p.rfs_year, p.decommission_year].filter(Boolean).join(' – ');
        layer.bindPopup(`
          <strong>${esc(p.name)}</strong><br>
          ${p.url ? `<a href="${esc(p.url)}" target="_blank" rel="noopener">${esc(p.url)}</a><br>` : ''}
          Owners: ${esc(p.owners)}<br>
          ${yrs ? `Years: ${esc(yrs)}<br>` : ''}
          ${p.length_km ? `Length: ${esc(p.length_km.toLocaleString())} km<br>` : ''}
          ID: ${esc(p.cable_id || p.feature_id)}
        `);
      },
    }).addTo(map);

    const cityLayer = L.layerGroup();
    for (const c of DATA.cities) {
      const tbps = c.tbps || 0;
      const radius = Math.max(2, Math.min(18, Math.sqrt(tbps) * 1.6));
      const m = L.circleMarker([c.lat, c.lon], {
        radius,
        color: '#6c2a86',
        fillColor: '#9b59b6',
        fillOpacity: 0.45,
        weight: 0.8,
      });
      m.bindPopup(`
        <strong>${esc(c.city)}</strong> (${esc(c.country)})<br>
        Region: ${esc(c.region)}<br>
        Physical capacity: ${esc(tbps.toFixed ? tbps.toFixed(2) : tbps)} Tbps<br>
        Logical dominance IPs: ${esc(c.ips ? c.ips.toLocaleString() : '')}
      `);
      cityLayer.addLayer(m);
    }
    cityLayer.addTo(map);

    const dcLayer = L.layerGroup();
    const dcBounds = [];
    for (const p of DATA.data_centers) {
      const m = L.circleMarker([p.lat, p.lon], {
        radius: 3,
        color: colorForSource(p.source),
        fillColor: colorForSource(p.source),
        fillOpacity: 0.85,
        weight: 0.8,
      });
      const title = p.name || p.id;
      const operator = p.operator || '(unknown operator)';
      const cityState = [p.city, p.state].filter(Boolean).join(', ');
      m.bindPopup(`
        <strong>${esc(title)}</strong><br>
        Operator: ${esc(operator)}<br>
        Location: ${esc(cityState)}<br>
        Source: ${esc(p.source)}
      `);
      dcLayer.addLayer(m);
      dcBounds.push([p.lat, p.lon]);
    }
    dcLayer.addTo(map);

    if (dcBounds.length) map.fitBounds(dcBounds, { padding: [30, 30], maxZoom: 5 });

    function toggle(layer, on) {
      if (on) { if (!map.hasLayer(layer)) layer.addTo(map); }
      else { if (map.hasLayer(layer)) map.removeLayer(layer); }
    }
    document.getElementById('tDc').addEventListener('change', e => toggle(dcLayer, e.target.checked));
    document.getElementById('tCables').addEventListener('change', e => toggle(cableLayer, e.target.checked));
    document.getElementById('tCities').addEventListener('change', e => toggle(cityLayer, e.target.checked));

    document.getElementById('dcCount').textContent = DATA.data_centers.length.toLocaleString();
    document.getElementById('cableCount').textContent = DATA.cables.features.length.toLocaleString();
    document.getElementById('cityCount').textContent = DATA.cities.length.toLocaleString();
  </script>
</body>
</html>
"""
    html = html.replace("__PAYLOAD__", payload)
    with open(output_path, "w", encoding="utf-8") as f:
        f.write(html)


def parse_args():
    p = argparse.ArgumentParser(
        description="Render a Leaflet map combining data centers, submarine cables, and city dominance."
    )
    p.add_argument("--output", default="data_centers_cables_map.html")
    p.add_argument(
        "--us-cities-only",
        action="store_true",
        help="Restrict the city-dominance layer to country='US'.",
    )
    return p.parse_args()


def main():
    args = parse_args()
    conn = connect()
    try:
        dcs = load_data_centers(conn)
        cables = load_cables(conn)
        cities = load_cities(conn, us_only=args.us_cities_only)
    finally:
        conn.close()

    render_html(dcs, cables, cities, args.output)
    print(
        f"wrote {len(dcs)} data centers, "
        f"{len(cables['features'])} cables, "
        f"{len(cities)} city points -> {args.output}"
    )


if __name__ == "__main__":
    main()
