{ lib, ... }:

let
  phones = import ./phones.nix { inherit lib; };
in {
  options.services.voip = {
    enable = lib.mkEnableOption "VoIP provisioning (Asterisk + TFTP)";

    serverAddress = lib.mkOption {
      type        = lib.types.str;
      description = "IP address or hostname of this server (used in phone configs and SIP).";
    };

    ntpServer = lib.mkOption {
      type        = lib.types.str;
      description = "NTP server for phones. Defaults to serverAddress.";
      default     = "";
    };

    sipPort = lib.mkOption {
      type    = lib.types.port;
      default = 5060;
    };

    rtpStart = lib.mkOption {
      type    = lib.types.port;
      default = 10000;
    };

    rtpEnd = lib.mkOption {
      type    = lib.types.port;
      default = 20000;
    };

    directoryName = lib.mkOption {
      type        = lib.types.str;
      default     = "tel.baubs.net";
      description = "Name shown in the phone directory title.";
    };

    directoryPort = lib.mkOption {
      type    = lib.types.port;
      default = 8080;
      description = "HTTP port for the phone directory and services.";
    };

    backgroundImages = lib.mkOption {
      default     = {};
      description = ''
        Attrset of background images keyed by display name.
        Value is a path to any image file — it will be resized automatically
        to the correct dimensions for each phone model during build.
        For best results, use a 4:3 aspect ratio source image.
      '';
      type = lib.types.attrsOf lib.types.path;
    };

    sharedPhones = lib.mkOption {
      default     = {};
      description = ''
        Shared/location phones not assigned to a specific person (e.g. hallway, kitchen).
        These have their own extension but no personal voicemail mailbox.
        For cisco-8961, the key must be the lowercase MAC address (no colons).
        For sip-client, the key is a free-form username.
      '';
      type = lib.types.attrsOf (lib.types.submodule {
        options = phones.phoneDeviceOptions false;
      });
    };

    persons = lib.mkOption {
      default     = {};
      description = "People with personal extensions, optional voicemail mailboxes, and their own phones.";
      type = lib.types.attrsOf (lib.types.submodule {
        options = {
          extension = lib.mkOption {
            type        = lib.types.str;
            description = "Personal extension number.";
          };
          displayName = lib.mkOption {
            type        = lib.types.str;
            default     = "";
            description = "Name shown in the directory and on caller ID.";
          };
          sharedLine = lib.mkOption {
            type        = lib.types.bool;
            default     = true;
            description = ''
              Whether this person's phones participate in the shared/family line (L2).
              Set to false for persons who should only be reachable on their personal
              extension and must not appear in "all" DID routing.
            '';
          };
          mailbox = lib.mkOption {
            type        = lib.types.bool;
            default     = true;
            description = "Whether this person gets a personal voicemail mailbox.";
          };
          ringTimeout = lib.mkOption {
            type        = lib.types.ints.positive;
            default     = 30;
            description = "Seconds to ring before going to voicemail (or hanging up if no mailbox).";
          };
          mailboxGreeting = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "Audio file played to callers before the voicemail beep. Transcoded to multiple formats at build time. null = Asterisk's default announcement.";
          };
          phones = lib.mkOption {
            default     = {};
            description = ''
              Phones belonging to this person, keyed by SIP identity.
              For cisco-8961, the key must be the lowercase MAC address (no colons).
              For sip-client, the key is a free-form username.
            '';
            type = lib.types.attrsOf (lib.types.submodule {
              options = phones.phoneDeviceOptions true;
            });
          };
        };
      });
    };

    intercomPrefix = lib.mkOption {
      type        = lib.types.nullOr lib.types.str;
      default     = null;
      description = "Dial prefix for auto-generated intercom extensions. e.g. \"*80\" generates *80100 for ext 100. Only intercom-capable (provisioned) phones get entries.";
    };

    mohClasses = lib.mkOption {
      default     = {};
      description = "Music on hold classes. Files are transcoded to ulaw at build time.";
      type        = lib.types.attrsOf (lib.types.submodule {
        options = {
          files = lib.mkOption {
            type        = lib.types.listOf lib.types.path;
            description = "Audio files to play (MP3, WAV, etc). Transcoded to 8kHz ulaw automatically.";
          };
          sort = lib.mkOption {
            type    = lib.types.enum [ "random" "alphabetical" ];
            default = "random";
          };
        };
      });
    };

    sipTrunks = lib.mkOption {
      default     = {};
      description = "External SIP provider trunks, keyed by a short name (e.g. \"provider-a\").";
      type        = lib.types.attrsOf (lib.types.submodule {
        options = {
          host = lib.mkOption {
            type        = lib.types.str;
            default     = "";
            description = "SIP provider hostname or IP address. Use hostFile to read from a file.";
          };
          hostFile = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "File containing the SIP provider hostname. Takes precedence over host.";
          };
          username = lib.mkOption {
            type        = lib.types.str;
            default     = "";
            description = "SIP account username. Use usernameFile to read from a file.";
          };
          usernameFile = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "File containing the SIP account username. Takes precedence over username.";
          };
          password = lib.mkOption {
            type        = lib.types.str;
            default     = "";
            description = "SIP account password. Use passwordFile to read from a file.";
          };
          passwordFile = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "File containing the SIP account password. Takes precedence over password.";
          };
          transport = lib.mkOption {
            type    = lib.types.enum [ "udp" "tcp" ];
            default = "udp";
          };
          callerId = lib.mkOption {
            type        = lib.types.str;
            default     = "";
            description = "Outbound caller ID number presented to the provider. Leave empty to use provider default. Use callerIdFile to read from a file.";
          };
          callerIdFile = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "File containing the outbound caller ID. Takes precedence over callerId.";
          };
          codecs = lib.mkOption {
            type        = lib.types.listOf lib.types.str;
            default     = [ "alaw" "ulaw" ];
            description = "Codec preference list for this trunk, ordered highest priority first. Most providers only support G.711.";
          };
        };
      });
    };

    sharedMailbox = lib.mkOption {
      default     = null;
      description = "Shared voicemail mailbox accessible by all phones (family answering machine).";
      type        = lib.types.nullOr (lib.types.submodule {
        options = {
          mailboxId = lib.mkOption {
            type        = lib.types.str;
            default     = "200";
            description = "Numeric mailbox ID used internally in voicemail.conf and VoiceMail(). Must be numeric.";
          };
          checkExtension = lib.mkOption {
            type        = lib.types.str;
            default     = "*98";
            description = "Extension dialled to check this shared mailbox directly (via VoiceMailMain).";
          };
          displayName = lib.mkOption {
            type        = lib.types.str;
            default     = "Shared";
            description = "Name shown in voicemail configuration.";
          };
          greeting = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "Audio file played to callers before the voicemail beep. Transcoded to multiple formats at build time. null = Asterisk's default announcement.";
          };
        };
      });
    };

    dids = lib.mkOption {
      default     = {};
      description = "Inbound DID routing. Each DID must reference a key from sipTrunks.";
      type        = lib.types.attrsOf (lib.types.submodule {
        options = {
          number = lib.mkOption {
            type        = lib.types.str;
            default     = "";
            description = "DID number in E.164 format (e.g. \"+4912345678\"). Use numberFile to read from a file.";
          };
          numberFile = lib.mkOption {
            type        = lib.types.nullOr lib.types.path;
            default     = null;
            description = "File containing the DID number. Takes precedence over number.";
          };
          trunk = lib.mkOption {
            type        = lib.types.str;
            description = "Key of the sipTrunks entry this DID arrives on.";
          };
          displayName = lib.mkOption {
            type    = lib.types.str;
            default = "";
            description = "Human-readable label for this DID (informational only).";
          };
          routing = lib.mkOption {
            description = "How inbound calls on this DID are distributed to phones.";
            type = lib.types.submodule {
              options = {
                type = lib.mkOption {
                  type        = lib.types.enum [ "all" "person" "persons" ];
                  description = ''
                    all     — ring all phones (sharedPhones + all persons) on their L2 line
                    person  — ring a single person on their L1 line
                    persons — ring a list of persons on their L2 line
                  '';
                };
                person = lib.mkOption {
                  type        = lib.types.str;
                  default     = "";
                  description = "Person key for routing.type = \"person\".";
                };
                persons = lib.mkOption {
                  type        = lib.types.listOf lib.types.str;
                  default     = [];
                  description = "Person keys for routing.type = \"persons\".";
                };
                timeout = lib.mkOption {
                  type        = lib.types.ints.positive;
                  default     = 30;
                  description = "Seconds to ring before going to voicemail (or hanging up).";
                };
              };
            };
          };
          mailbox = lib.mkOption {
            type        = lib.types.enum [ "shared" "person" "none" ];
            default     = "shared";
            description = ''
              shared — go to sharedMailbox on no answer (requires sharedMailbox to be set)
              person — go to the routed person's mailbox on no answer (only valid with routing.type = "person")
              none   — hang up on no answer
            '';
          };
          musicOnHold = lib.mkOption {
            type        = lib.types.nullOr lib.types.str;
            default     = null;
            description = ''
              MOH class name to play to the caller while phones ring, instead of ringback.
              Must match a key in mohClasses. null = standard ringback.
            '';
          };
        };
      });
    };

    extensions = lib.mkOption {
      default     = {};
      description = ''
        Extra extensions: page groups and custom app entries.
        Line extensions are auto-generated from sharedPhones and persons — do not declare them here.
      '';
      type        = lib.types.attrsOf (lib.types.submodule {
        options = {
          mode = lib.mkOption {
            type        = lib.types.enum [ "page" "all" "app" ];
            default     = "page";
            description = ''
              Extension mode:
              - "page": one-way announcement to all intercom-capable phones
              - "all":  two-way call ringing all shared-line phones (allL2Endpoints)
              - "app":  custom Asterisk dialplan application
            '';
          };
          displayName = lib.mkOption {
            type    = lib.types.str;
            default = "";
          };
          app = lib.mkOption {
            type        = lib.types.nullOr lib.types.str;
            default     = null;
            description = "Verbatim Asterisk dialplan app. Required for mode = \"app\".";
          };
        };
      });
    };
  };
}
