{ lib }:

let
  # Per-model config. Adding a new hardware model:
  #   1. Add an entry here (PBX-relevant fields only)
  #   2. Add a provisioning template in ./provisioning/templates/<model>.nix
  #      exporting: desktopSize, thumbnailSize, mkConfig, mkDialplan
  models = {
    "cisco-8961" = {
      endpointTemplate = "endpoint-cisco-8961";
      maxContacts      = 1;
      hasProvisioning  = true;
      hasIntercom      = true;   # auto-answer speakerphone line
      hasMultiLine     = true;   # separate L2 line for family/shared DID
    };
    "sip-client" = {
      endpointTemplate = "endpoint-generic";
      maxContacts      = 1;
      hasProvisioning  = false;
      hasIntercom      = false;
      hasMultiLine     = false;
    };
  };

  # Shared option set for a physical phone device.
  # isPersonPhone = true  → no extension/displayName fields (inherited from person)
  # isPersonPhone = false → includes extension and displayName
  phoneDeviceOptions = isPersonPhone: {
    model = lib.mkOption {
      type        = lib.types.enum (lib.attrNames models);
      description = "Phone model. Use \"sip-client\" for software SIP clients (no provisioning file).";
    };
    label = lib.mkOption {
      type        = lib.types.str;
      default     = "";
      description = "Label shown on the phone screen (max ~12 chars for Cisco). Required for provisioned hardware phones.";
    };
    password = lib.mkOption {
      type        = lib.types.str;
      description = "SIP registration password.";
    };
  } // lib.optionalAttrs (!isPersonPhone) {
    extension = lib.mkOption {
      type        = lib.types.str;
      description = "Internal extension number for this shared phone.";
    };
    displayName = lib.mkOption {
      type        = lib.types.str;
      default     = "";
      description = "Name shown in the phone directory.";
    };
  };

  # Unified view of all physical devices, keyed by SIP identity (MAC or username).
  # Each entry carries the fields needed by sub-modules without them having to
  # know about sharedPhones vs persons.
  mkAllPhones = cfg:
    lib.mapAttrs (key: p: {
      inherit (p) model label password;
      extension   = p.extension;
      displayName = p.displayName;
      personKey   = null;
      mailboxExt  = null;   # shared phones have no personal mailbox
    }) cfg.sharedPhones
    //
    lib.foldlAttrs (acc: personKey: person:
      acc // lib.mapAttrs (_key: ph: {
        inherit (ph) model label password;
        extension   = person.extension;
        displayName = person.displayName;
        personKey   = personKey;
        mailboxExt  = if person.mailbox then person.extension else null;
      }) person.phones
    ) {} cfg.persons;

  # Auto-generate intercom extensions for provisioned phones.
  mkIntercomEntries = cfg: allPhones:
    if cfg.intercomPrefix == null then []
    else lib.concatLists (lib.mapAttrsToList (key: phone:
      let m = models.${phone.model}; in
      lib.optional m.hasIntercom {
        extension        = "${cfg.intercomPrefix}${phone.extension}";
        endpoint         = "${key}-intercom";
        phoneKey         = key;
        target           = phone.extension;
        displayName      = "Intercom ${phone.displayName}";
        password         = phone.password;
        endpointTemplate = m.endpointTemplate;
        maxContacts      = m.maxContacts;
      }
    ) allPhones);

in {
  inherit models phoneDeviceOptions mkAllPhones mkIntercomEntries;
}
