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

with lib;

let
  cfg = config.nix-nos.network;
  ifaceCfg = config.nix-nos.interfaces;

  interfaceType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "Interface name (e.g., eth0, ens3)";
      };

      addresses = mkOption {
        type = types.listOf types.str;
        default = [];
        description = "List of IP addresses in CIDR notation";
        example = [ "192.168.1.10/24" "2001:db8::1/64" ];
      };

      mtu = mkOption {
        type = types.nullOr types.int;
        default = null;
        description = "Maximum Transmission Unit (MTU) size";
        example = 9000;
      };

      vlan = mkOption {
        type = types.nullOr types.int;
        default = null;
        description = "VLAN ID if this is a VLAN interface";
      };
    };
  };

  # New systemd-networkd based interface type
  systemdInterfaceType = types.submodule {
    options = {
      addresses = mkOption {
        type = types.listOf types.str;
        default = [];
        description = "IP addresses with CIDR (e.g., 192.168.1.10/24)";
        example = [ "192.168.1.10/24" "2001:db8::1/64" ];
      };

      gateway = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Default gateway for this interface";
        example = "192.168.1.1";
      };

      dns = mkOption {
        type = types.listOf types.str;
        default = [];
        description = "DNS servers";
        example = [ "8.8.8.8" "8.8.4.4" ];
      };

      dhcp = mkOption {
        type = types.bool;
        default = false;
        description = "Enable DHCP on this interface";
      };

      mtu = mkOption {
        type = types.nullOr types.int;
        default = null;
        description = "MTU size";
        example = 1500;
      };
    };
  };

in {
  options.nix-nos.interfaces = mkOption {
    type = types.attrsOf systemdInterfaceType;
    default = {};
    description = "Network interface configurations using systemd-networkd";
    example = literalExpression ''
      {
        eth0 = {
          addresses = [ "192.168.1.10/24" ];
          gateway = "192.168.1.1";
          dns = [ "8.8.8.8" "8.8.4.4" ];
        };
        eth1 = {
          dhcp = true;
        };
      }
    '';
  };

  options.nix-nos.network = {
    interfaces = mkOption {
      type = types.listOf interfaceType;
      default = [];
      description = "Network interface configurations";
      example = literalExpression ''
        [
          {
            name = "eth0";
            addresses = [ "10.0.0.10/24" ];
            mtu = 1500;
          }
          {
            name = "eth0.100";
            addresses = [ "192.168.100.1/24" ];
            vlan = 100;
          }
        ]
      '';
    };

    enableIpForwarding = mkOption {
      type = types.bool;
      default = false;
      description = "Enable IP forwarding (routing)";
    };
  };

  config = mkMerge [
    # Map nix-nos.interfaces to systemd-networkd
    (mkIf (ifaceCfg != {}) {
      assertions = [
        {
          assertion = all (name: iface:
            (iface.dhcp || (length iface.addresses) > 0)
          ) (mapAttrsToList nameValuePair ifaceCfg);
          message = "nix-nos.interfaces: Each interface must have either dhcp=true or at least one address";
        }
      ];

      systemd.network.enable = true;
      systemd.network.networks = mapAttrs' (name: iface: nameValuePair "10-${name}" {
        matchConfig.Name = name;

        address = iface.addresses;

        gateway = optional (iface.gateway != null) iface.gateway;

        dns = iface.dns;

        DHCP = if iface.dhcp then "yes" else "no";

        linkConfig = optionalAttrs (iface.mtu != null) {
          MTUBytes = toString iface.mtu;
        };
      }) ifaceCfg;
    })

    # Legacy: Map nix-nos.network.interfaces to NixOS networking.interfaces
    (mkIf config.nix-nos.enable {
      networking.interfaces = mkMerge (
        map (iface: {
          ${iface.name} = {
            ipv4.addresses = map (addr:
              let
                parts = splitString "/" addr;
                ip = elemAt parts 0;
                prefixLength = toInt (elemAt parts 1);
              in {
                address = ip;
                inherit prefixLength;
              }
            ) (filter (addr: ! (hasInfix ":" addr)) iface.addresses);

            ipv6.addresses = map (addr:
              let
                parts = splitString "/" addr;
                ip = elemAt parts 0;
                prefixLength = toInt (elemAt parts 1);
              in {
                address = ip;
                inherit prefixLength;
              }
            ) (filter (addr: hasInfix ":" addr) iface.addresses);

            mtu = mkIf (iface.mtu != null) iface.mtu;
          };
        }) cfg.interfaces
      );

      # Enable IP forwarding if requested
      boot.kernel.sysctl = mkIf cfg.enableIpForwarding {
        "net.ipv4.ip_forward" = 1;
        "net.ipv6.conf.all.forwarding" = 1;
      };
    })
  ];
}
