/**
 * WS3: Transport System Planner
 *
 * "Berlin to Darmstadt, 100G" → complete BOM with switches, fiber providers, Flexoptix transceivers
 */
import { Router } from "express";
import { pool } from "../db/client";

export const transportRouter = Router();

// Haversine distance calculation
function haversineKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
  const R = 6371;
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLon = (lon2 - lon1) * Math.PI / 180;
  const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180) * Math.cos(lat2*Math.PI/180) * Math.sin(dLon/2)**2;
  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}

/**
 * POST /api/transport/plan
 * Body: { from, to, bandwidth_gbps, redundancy?, budget_preference? }
 */
transportRouter.post("/plan", async (req, res) => {
  try {
    const { from, to, bandwidth_gbps = 100, redundancy = false, budget_preference = "balanced" } = req.body;

    if (!from || !to) {
      return res.status(400).json({ error: "Parameters 'from' and 'to' are required" });
    }

    // 1. Resolve cities
    const cityA = await pool.query(`SELECT * FROM cities WHERE name ILIKE $1 LIMIT 1`, [from]);
    const cityB = await pool.query(`SELECT * FROM cities WHERE name ILIKE $1 LIMIT 1`, [to]);

    if (!cityA.rows[0] || !cityB.rows[0]) {
      const allCities = await pool.query(`SELECT name, country FROM cities ORDER BY name`);
      return res.status(404).json({
        error: `City not found: ${!cityA.rows[0] ? from : to}`,
        available_cities: allCities.rows.map(c => `${c.name} (${c.country})`),
      });
    }

    const a = cityA.rows[0];
    const b = cityB.rows[0];

    // 2. Calculate distance
    const straightKm = haversineKm(parseFloat(a.lat), parseFloat(a.lon), parseFloat(b.lat), parseFloat(b.lon));
    const fiberKm = Math.round(straightKm * 1.4); // fiber route multiplier

    // 3. Determine transceiver requirements based on distance
    const transceiverOptions = determineTransceiverOptions(fiberKm, bandwidth_gbps);

    // 4. Find fiber providers for this route
    const providers = await pool.query(
      `SELECT fp.name, fp.website, fp.type, fp.products,
              fr.product_type, fr.monthly_price_eur, fr.setup_fee_eur, fr.min_contract_months
       FROM fiber_routes fr
       JOIN fiber_providers fp ON fr.provider_id = fp.id
       WHERE (fr.city_a ILIKE $1 AND fr.city_b ILIKE $2)
          OR (fr.city_a ILIKE $2 AND fr.city_b ILIKE $1)
          OR (fr.city_a ILIKE $1 AND fr.city_b ILIKE 'Frankfurt%')
          OR (fr.city_a ILIKE 'Frankfurt%' AND fr.city_b ILIKE $2)
       ORDER BY fr.monthly_price_eur ASC NULLS LAST`,
      [from, to]
    );

    // 5. Find matching switches
    const switchOptions = await pool.query(
      `SELECT sw.id, sw.model, sw.series, sw.max_speed_gbps, sw.switching_capacity_tbps,
              sw.ports_config, sw.msrp_usd, v.name AS vendor
       FROM switches sw JOIN vendors v ON sw.vendor_id = v.id
       WHERE sw.max_speed_gbps >= $1
         AND sw.lifecycle_status NOT IN ('End-of-Life', 'End-of-Sale')
       ORDER BY sw.msrp_usd ASC NULLS LAST, sw.max_speed_gbps DESC
       LIMIT 10`,
      [bandwidth_gbps]
    );

    // 6. Find Flexoptix transceivers for each option
    const options = [];
    for (const tcvrOpt of transceiverOptions) {
      const flexoptix = await pool.query(
        `SELECT t.id, t.slug, t.form_factor, t.speed_gbps, t.reach_label, t.reach_meters,
                t.fiber_type, t.connector, t.image_url,
                (SELECT po.price FROM price_observations po WHERE po.transceiver_id = t.id ORDER BY po.time DESC LIMIT 1) AS price,
                (SELECT po.currency FROM price_observations po WHERE po.transceiver_id = t.id ORDER BY po.time DESC LIMIT 1) AS currency
         FROM transceivers t
         JOIN vendors v ON t.vendor_id = v.id
         WHERE t.speed_gbps >= $1
           AND t.reach_meters >= $2
           AND t.fiber_type = 'SMF'
           AND v.slug = 'flexoptix'
         ORDER BY t.speed_gbps ASC, t.reach_meters ASC
         LIMIT 5`,
        [tcvrOpt.speed_gbps, tcvrOpt.min_reach_m]
      );

      // If no Flexoptix match, find any compatible transceiver
      const anyMatch = flexoptix.rows.length > 0 ? flexoptix.rows : (await pool.query(
        `SELECT t.id, t.slug, t.form_factor, t.speed_gbps, t.reach_label, t.reach_meters,
                t.fiber_type, t.connector, t.image_url, v.name AS vendor,
                (SELECT po.price FROM price_observations po WHERE po.transceiver_id = t.id ORDER BY po.time DESC LIMIT 1) AS price
         FROM transceivers t JOIN vendors v ON t.vendor_id = v.id
         WHERE t.speed_gbps >= $1 AND t.reach_meters >= $2 AND t.fiber_type = 'SMF'
         ORDER BY t.speed_gbps ASC LIMIT 5`,
        [tcvrOpt.speed_gbps, tcvrOpt.min_reach_m]
      )).rows;

      const spanCount = Math.ceil(fiberKm * 1000 / tcvrOpt.max_span_m);
      const tcvrCount = redundancy ? spanCount * 4 : spanCount * 2; // 2 per span (both ends), x2 for redundancy

      const tcvrPrice = anyMatch[0]?.price ? parseFloat(anyMatch[0].price) : tcvrOpt.est_price_eur;
      const totalTcvrCost = tcvrCount * tcvrPrice;

      options.push({
        name: tcvrOpt.name,
        description: tcvrOpt.description,
        transceiver: {
          type: `${tcvrOpt.speed_gbps}G ${tcvrOpt.reach_label}`,
          form_factor: tcvrOpt.form_factor,
          spans_needed: spanCount,
          units_needed: tcvrCount,
          unit_price_est: tcvrPrice,
          total_cost_est: totalTcvrCost,
          flexoptix_products: anyMatch.map(m => ({
            slug: m.slug,
            speed: m.speed_gbps + 'G',
            reach: m.reach_label,
            price: m.price ? parseFloat(m.price) : null,
            buy_url: `https://www.flexoptix.net/en/catalogsearch/result/?q=${encodeURIComponent(m.form_factor + ' ' + m.speed_gbps + 'G ' + m.reach_label)}`,
          })),
        },
        switches: switchOptions.rows.slice(0, 3).map(sw => ({
          model: sw.model,
          vendor: sw.vendor,
          max_speed: sw.max_speed_gbps + 'G',
          price_est: sw.msrp_usd ? parseFloat(sw.msrp_usd) : null,
        })),
        fiber_providers: providers.rows.length > 0 ? providers.rows : [
          { name: "Contact local fiber providers", note: `No pre-seeded routes for ${from}↔${to}. Check euNetworks, Telia, DTAG.` }
        ],
      });
    }

    res.json({
      route: {
        from: a.name,
        to: b.name,
        straight_line_km: Math.round(straightKm),
        estimated_fiber_km: fiberKm,
        bandwidth_requested: bandwidth_gbps + 'G',
        redundancy,
      },
      options,
      note: "Prices are estimates. Contact Flexoptix sales for volume pricing.",
    });
  } catch (err) {
    console.error("Transport planner error:", err);
    res.status(500).json({ error: "Internal server error" });
  }
});

function determineTransceiverOptions(fiberKm: number, bandwidthGbps: number) {
  const options = [];

  if (fiberKm <= 2) {
    options.push({
      name: `${bandwidthGbps}G FR (2km)`,
      description: `Short reach — single span, no amplification needed`,
      speed_gbps: bandwidthGbps, reach_label: 'FR', form_factor: bandwidthGbps >= 400 ? 'QSFP-DD' : 'QSFP28',
      min_reach_m: 2000, max_span_m: 2000, est_price_eur: bandwidthGbps >= 400 ? 200 : 80,
    });
  }
  if (fiberKm <= 10) {
    options.push({
      name: `${bandwidthGbps}G LR4 (10km)`,
      description: `Metro reach — ${Math.ceil(fiberKm / 10)} span(s)`,
      speed_gbps: bandwidthGbps, reach_label: 'LR4', form_factor: bandwidthGbps >= 400 ? 'QSFP-DD' : 'QSFP28',
      min_reach_m: 10000, max_span_m: 10000, est_price_eur: bandwidthGbps >= 400 ? 400 : 120,
    });
  }
  if (fiberKm <= 40) {
    options.push({
      name: `${bandwidthGbps}G ER4 (40km)`,
      description: `Extended reach — ${Math.ceil(fiberKm / 40)} span(s)`,
      speed_gbps: bandwidthGbps, reach_label: 'ER4', form_factor: bandwidthGbps >= 400 ? 'QSFP-DD' : 'QSFP28',
      min_reach_m: 40000, max_span_m: 40000, est_price_eur: bandwidthGbps >= 400 ? 1500 : 400,
    });
  }
  // ZR is always an option for long distances
  if (fiberKm > 10) {
    options.push({
      name: `${Math.min(bandwidthGbps, 400)}G ZR Coherent (80km/span)`,
      description: `Coherent DWDM — ${Math.ceil(fiberKm / 80)} span(s), OIF 400ZR`,
      speed_gbps: Math.min(bandwidthGbps, 400), reach_label: 'ZR', form_factor: 'QSFP-DD',
      min_reach_m: 80000, max_span_m: 80000, est_price_eur: 2500,
    });
  }
  // Carrier wavelength option
  options.push({
    name: `Carrier Wavelength Service (${bandwidthGbps}G)`,
    description: `Managed service — provider handles fiber + amplification. You only need LR4 transceivers at each end.`,
    speed_gbps: bandwidthGbps, reach_label: 'LR4', form_factor: bandwidthGbps >= 400 ? 'QSFP-DD' : 'QSFP28',
    min_reach_m: 10000, max_span_m: 999000, est_price_eur: bandwidthGbps >= 400 ? 400 : 120,
  });

  return options;
}

/**
 * GET /api/transport/cities
 */
transportRouter.get("/cities", async (_req, res) => {
  try {
    const result = await pool.query(`SELECT name, country, has_ix, ix_names, has_datacenter FROM cities ORDER BY name`);
    res.json({ cities: result.rows, total: result.rowCount });
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
});

/**
 * GET /api/transport/providers
 */
transportRouter.get("/providers", async (_req, res) => {
  try {
    const result = await pool.query(`SELECT * FROM fiber_providers ORDER BY name`);
    res.json({ providers: result.rows, total: result.rowCount });
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
});
