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

with lib;

let
  cfg = config.ultracloud.tenantNetworking;
  jsonFormat = pkgs.formats.json {};

  serviceIpPoolType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "Service IP pool name";
      };

      cidr_block = mkOption {
        type = types.str;
        description = "Service IP pool CIDR";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Service IP pool description";
      };

      pool_type = mkOption {
        type = types.nullOr (types.enum [ "cluster_ip" "load_balancer" "node_port" ]);
        default = null;
        description = "Service IP pool type";
      };
    };
  };

  portType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "Port name";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Port description";
      };

      ip_address = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Requested fixed IP address";
      };

      security_groups = mkOption {
        type = types.listOf types.str;
        default = [];
        description = "Security group names attached to the port";
      };

      admin_state_up = mkOption {
        type = types.nullOr types.bool;
        default = null;
        description = "Administrative state for the port";
      };
    };
  };

  subnetType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "Subnet name";
      };

      cidr_block = mkOption {
        type = types.str;
        description = "Subnet CIDR";
      };

      gateway_ip = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Gateway IP";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Subnet description";
      };

      dhcp_enabled = mkOption {
        type = types.nullOr types.bool;
        default = null;
        description = "Enable DHCP for the subnet";
      };

      ports = mkOption {
        type = types.listOf portType;
        default = [];
        description = "Ports within the subnet";
      };
    };
  };

  routerType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "Router name";
      };

      gateway_cidr = mkOption {
        type = types.str;
        description = "Gateway interface CIDR attached to the VPC";
      };

      mac_address = mkOption {
        type = types.str;
        description = "Router interface MAC address";
      };

      external_ip = mkOption {
        type = types.str;
        description = "SNAT external IPv4 address";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Router description";
      };
    };
  };

  vpcType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "VPC name";
      };

      cidr_block = mkOption {
        type = types.str;
        description = "VPC CIDR";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "VPC description";
      };

      router = mkOption {
        type = types.nullOr routerType;
        default = null;
        description = "Optional tenant edge router for the VPC";
      };

      subnets = mkOption {
        type = types.listOf subnetType;
        default = [];
        description = "Subnets within the VPC";
      };
    };
  };

  securityGroupRuleType = types.submodule {
    options = {
      direction = mkOption {
        type = types.enum [ "ingress" "egress" ];
        description = "Rule direction";
      };

      protocol = mkOption {
        type = types.nullOr (types.enum [ "any" "tcp" "udp" "icmp" "icmpv6" ]);
        default = null;
        description = "IP protocol";
      };

      port_range_min = mkOption {
        type = types.nullOr types.int;
        default = null;
        description = "Minimum port in range";
      };

      port_range_max = mkOption {
        type = types.nullOr types.int;
        default = null;
        description = "Maximum port in range";
      };

      remote_cidr = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Remote CIDR";
      };

      remote_group = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Remote security group name";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Rule description";
      };
    };
  };

  securityGroupType = types.submodule {
    options = {
      name = mkOption {
        type = types.str;
        description = "Security group name";
      };

      description = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "Security group description";
      };

      rules = mkOption {
        type = types.listOf securityGroupRuleType;
        default = [];
        description = "Security group rules";
      };
    };
  };

  tenantType = types.submodule {
    options = {
      org_id = mkOption {
        type = types.str;
        description = "Tenant organization ID";
      };

      project_id = mkOption {
        type = types.str;
        description = "Tenant project ID";
      };

      security_groups = mkOption {
        type = types.listOf securityGroupType;
        default = [];
        description = "Tenant-scoped security groups";
      };

      service_ip_pools = mkOption {
        type = types.listOf serviceIpPoolType;
        default = [];
        description = "Tenant-scoped Service IP pools";
      };

      vpcs = mkOption {
        type = types.listOf vpcType;
        default = [];
        description = "Tenant-scoped VPCs and their nested resources";
      };
    };
  };

  configFile = jsonFormat.generate "ultracloud-tenant-networking.json" {
    inherit (cfg) tenants;
  };
  configPath = cfg.configPath;
  configRelative = removePrefix "/etc/" configPath;

in {
  options.ultracloud.tenantNetworking = {
    enable = mkEnableOption "tenant-scoped PrismNET declarations";

    endpoint = mkOption {
      type = types.str;
      default = "http://127.0.0.1:50081";
      description = "PrismNET gRPC endpoint";
    };

    iamEndpoint = mkOption {
      type = types.str;
      default = "http://127.0.0.1:50080";
      description = "IAM gRPC endpoint used to mint tenant-scoped controller tokens";
    };

    controllerPrincipalId = mkOption {
      type = types.str;
      default = "ultracloud-reconciler";
      description = "Service account used by the reconciler when applying tenant declarations";
    };

    tenants = mkOption {
      type = types.listOf tenantType;
      default = [];
      description = "Tenant-scoped network declarations. This is separate from platform networking under ultracloud.network.";
    };

    configPath = mkOption {
      type = types.str;
      default = "/etc/ultracloud/tenant-networking.json";
      description = "Path for rendered tenant networking config";
    };

    applyOnBoot = mkOption {
      type = types.bool;
      default = true;
      description = "Apply declarations at boot";
    };

    applyOnChange = mkOption {
      type = types.bool;
      default = true;
      description = "Apply declarations when the config file changes";
    };

    prune = mkOption {
      type = types.bool;
      default = false;
      description = "Delete tenant network resources not declared for managed tenants";
    };

    package = mkOption {
      type = types.package;
      default = pkgs.ultracloud-reconciler or (throw "ultracloud-reconciler package not found");
      description = "Reconciler package for tenant networking declarations";
    };
  };

  config = mkIf cfg.enable {
    assertions = [
      {
        assertion = hasPrefix "/etc/" configPath;
        message = "ultracloud.tenantNetworking.configPath must be under /etc";
      }
    ];

    environment.etc."${configRelative}".source = configFile;

    systemd.services.ultracloud-tenant-networking-apply = {
      description = "Apply UltraCloud tenant networking declarations";
      after =
        [ "network-online.target" ]
        ++ optional config.services.prismnet.enable "prismnet.service"
        ++ optional config.services.iam.enable "iam.service";
      wants =
        [ "network-online.target" ]
        ++ optional config.services.prismnet.enable "prismnet.service"
        ++ optional config.services.iam.enable "iam.service";
      wantedBy = optional cfg.applyOnBoot "multi-user.target";

      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
        ExecStart =
          "${cfg.package}/bin/ultracloud-reconciler tenant-network"
          + " --config ${configPath}"
          + " --endpoint ${cfg.endpoint}"
          + " --iam-endpoint ${cfg.iamEndpoint}"
          + " --controller-principal-id ${cfg.controllerPrincipalId}"
          + optionalString cfg.prune " --prune";
      };
    };

    systemd.paths.ultracloud-tenant-networking-apply = mkIf cfg.applyOnChange {
      wantedBy = [ "multi-user.target" ];
      pathConfig = {
        PathChanged = configPath;
      };
    };
  };
}
