{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.ultracloud.network;
  clusterCfg = config.ultracloud.cluster;
  hostName = config.networking.hostName;
  clusterNode =
    if clusterCfg.enable or false && clusterCfg.nodes ? "${hostName}"
    then clusterCfg.nodes.${hostName}
    else null;
  clusterNodeIp =
    if clusterNode != null
    then clusterNode.ip
    else "127.0.0.1";

  # BGP peer type for FiberLB
  bgpPeerType = types.submodule {
    options = {
      address = mkOption {
        type = types.str;
        description = "Peer IP address (ToR switch, upstream router)";
        example = "192.168.1.1";
      };

      asn = mkOption {
        type = types.int;
        description = "Peer AS number";
        example = 65001;
      };

      description = mkOption {
        type = types.str;
        default = "";
        description = "Human-readable peer description";
        example = "ToR switch rack1";
      };
    };
  };

in
{
  options.ultracloud.network = {
    fiberlbBgp = {
      enable = mkEnableOption "FiberLB BGP VIP advertisement";

      vips = mkOption {
        type = types.listOf types.str;
        default = [ ];
        description = "Legacy static VIP hints. FiberLB native BGP ignores this list and advertises active load balancer VIPs dynamically.";
        example = [ "203.0.113.10/32" "203.0.113.11/32" ];
      };

      peers = mkOption {
        type = types.listOf bgpPeerType;
        default = [ ];
        description = "BGP peers (ToR switches, upstream routers)";
        example = literalExpression ''
          [
            { address = "192.168.1.1"; asn = 65001; description = "ToR switch rack1"; }
            { address = "192.168.1.2"; asn = 65001; description = "ToR switch rack2"; }
          ]
        '';
      };

      routerId = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "BGP router ID (auto-detected from primary IP if null)";
      };
    };

    prismnetIntegration = {
      enable = mkEnableOption "PrismNET OVN integration";

      encapType = mkOption {
        type = types.enum [ "geneve" "vxlan" ];
        default = "geneve";
        description = "Encapsulation type reserved for future OVN controller wiring.";
      };

      encapIp = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Explicit OVN encapsulation IP. Defaults to the node IP when future controller wiring is added.";
      };
    };
  };

  config = mkMerge [
    # FiberLB BGP configuration
    (mkIf cfg.fiberlbBgp.enable {
      # Assertions
      assertions = [
        {
          assertion = clusterCfg.bgp.asn > 0;
          message = "ultracloud.cluster.bgp.asn must be configured for FiberLB BGP";
        }
        {
          assertion = (length cfg.fiberlbBgp.peers) > 0;
          message = "ultracloud.network.fiberlbBgp.peers must contain at least one BGP peer";
        }
        {
          assertion = config.services.fiberlb.enable or false;
          message = "ultracloud.network.fiberlbBgp.enable requires services.fiberlb.enable";
        }
      ];

      services.fiberlb.bgp = {
        enable = true;
        localAs = clusterCfg.bgp.asn;
        routerId =
          if cfg.fiberlbBgp.routerId != null
          then cfg.fiberlbBgp.routerId
          else clusterNodeIp;
        peers = cfg.fiberlbBgp.peers;
      };

      services.fiberlb.vipOwnership.enable = mkDefault true;
    })

    # PrismNET OVN integration
    (mkIf cfg.prismnetIntegration.enable {
      virtualisation.vswitch.enable = true;
      environment.systemPackages = [ pkgs.ovn ];
      warnings = [
        "ultracloud.network.prismnetIntegration.enable currently enables Open vSwitch and installs OVN tooling only. Wire ovn-controller explicitly before expecting PrismNET dataplane automation."
      ];
    })
  ];
}
