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

with lib;

let

  cfg = config.services.aiccu;
  showBool = b: if b then "true" else "false";
  notNull = a: ! isNull a;
  configFile = pkgs.writeText "aiccu.conf" ''
    ${if notNull cfg.username then "username " + cfg.username else ""}
    ${if notNull cfg.password then "password " + cfg.password else ""}
    protocol ${cfg.protocol}
    server ${cfg.server}
    ipv6_interface ${cfg.interfaceName}
    verbose ${showBool cfg.verbose}
    daemonize true
    automatic ${showBool cfg.automatic}
    requiretls ${showBool cfg.requireTLS}
    pidfile ${cfg.pidFile}
    defaultroute ${showBool cfg.defaultRoute}
    ${if notNull cfg.setupScript then cfg.setupScript else ""}
    makebeats ${showBool cfg.makeHeartBeats}
    noconfigure ${showBool cfg.noConfigure}
    behindnat ${showBool cfg.behindNAT}
    ${if cfg.localIPv4Override then "local_ipv4_override" else ""}
  '';

in {

  options = {

    services.aiccu = {

      enable = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = "Enable aiccu IPv6 over IPv4 SiXXs tunnel";
      };

      username = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "FAB5-SIXXS";
        description = "Login credential";
      };

      password = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "TmAkRbBEr0";
        description = "Login credential";
      };

      protocol = mkOption {
        type = types.str;
        default = "tic";
        example = "tic|tsp|l2tp";
        description = "Protocol to use for setting up the tunnel";
      };

      server = mkOption {
        type = types.str;
        default = "tic.sixxs.net";
        example = "enabled.ipv6server.net";
        description = "Server to use for setting up the tunnel";
      };

      interfaceName = mkOption {
        type = types.str;
        default = "aiccu";
        example = "sixxs";
        description = ''
          The name of the interface that will be used as a tunnel interface.
          On *BSD the ipv6_interface should be set to gifX (eg gif0) for proto-41 tunnels
          or tunX (eg tun0) for AYIYA tunnels.
        '';
      };

      tunnelID = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "T12345";
        description = "The tunnel id to use, only required when there are multiple tunnels in the list";
      };

      verbose = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = "Be verbose?";
      };

      automatic = mkOption {
        type = types.bool;
        default = true;
        example = false;
        description = "Automatic Login and Tunnel activation";
      };

      requireTLS = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = ''
          When set to true, if TLS is not supported on the server
          the TIC transaction will fail.
          When set to false, it will try a starttls, when that is
          not supported it will continue.
          In any case if AICCU is build with TLS support it will
          try to do a 'starttls' to the TIC server to see if that
          is supported.
        '';
      };

      pidFile = mkOption {
        type = types.path;
        default = "/run/aiccu.pid";
        example = "/var/lib/aiccu/aiccu.pid";
        description = "Location of PID File";
      };

      defaultRoute = mkOption {
        type = types.bool;
        default = true;
        example = false;
        description = "Add a default route";
      };

      setupScript = mkOption {
        type = with types; nullOr path;
        default = null;
        example = "/var/lib/aiccu/fix-subnets.sh";
        description = "Script to run after setting up the interfaces";
      };

      makeHeartBeats = mkOption {
        type = types.bool;
        default = true;
        example = false;
        description = ''
          In general you don't want to turn this off
          Of course only applies to AYIYA and heartbeat tunnels not to static ones
        '';
      };

      noConfigure = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = "Don't configure anything";
      };

      behindNAT = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = "Notify the user that a NAT-kind network is detected";
      };

      localIPv4Override = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = ''
          Overrides the IPv4 parameter received from TIC
          This allows one to configure a NAT into "DMZ" mode and then
          forwarding the proto-41 packets to an internal host.

          This is only needed for static proto-41 tunnels!
          AYIYA and heartbeat tunnels don't require this.
        '';
      };

    };
  };

  config = mkIf cfg.enable {

    systemd.services.aiccu = {
      description = "Automatic IPv6 Connectivity Client Utility";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = "${pkgs.aiccu}/bin/aiccu start ${configFile}";
        ExecStop = "${pkgs.aiccu}/bin/aiccu stop";
        Type = "forking";
        PIDFile = cfg.pidFile;
        Restart = "no"; # aiccu startup errors are serious, do not pound the tic server or be banned.
      };
    };

  };
}