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

let
  cfg = config.services.flaredb;
  tomlFormat = pkgs.formats.toml { };
  stripLeadingZeros = digits:
    if digits == "" then ""
    else if lib.hasPrefix "0" digits then stripLeadingZeros (lib.removePrefix "0" digits)
    else digits;
  numericIdString = value:
    let
      captures = builtins.match ".*?([0-9]+)$" value;
      digits =
        if captures == null
        then throw "services.flaredb.nodeId must end with digits (got '${value}')"
        else builtins.elemAt captures 0;
      normalized = stripLeadingZeros digits;
    in
    if normalized == "" then "0" else normalized;
  apiAddrArg =
    if cfg.apiAddr != null
    then cfg.apiAddr
    else "0.0.0.0:${toString cfg.port}";
  peerArgs = map
    (peer:
      let
        parts = lib.splitString "=" peer;
        rawId =
          if builtins.length parts == 2
          then builtins.elemAt parts 0
          else throw "services.flaredb.initialPeers entries must be 'storeId=host:port' (got '${peer}')";
        peerAddr = builtins.elemAt parts 1;
      in
      "--peer ${numericIdString rawId}=${peerAddr}")
    cfg.initialPeers;
  chainfirePeerApiEndpoints =
    if (config.services ? chainfire) && config.services.chainfire.enable
    then
      map
        (peer:
          let
            parts = lib.splitString "=" peer;
            raftAddr =
              if builtins.length parts == 2
              then builtins.elemAt parts 1
              else throw "services.chainfire.initialPeers entries must be 'nodeId=host:port' (got '${peer}')";
            captures = builtins.match "(.+):[0-9]+" raftAddr;
            host =
              if captures == null
              then throw "services.chainfire.initialPeers raft address must be host:port (got '${raftAddr}')"
              else builtins.elemAt captures 0;
          in
          "${host}:${toString config.services.chainfire.port}")
        config.services.chainfire.initialPeers
    else [];
  defaultPdAddr =
    if cfg.pdAddr != null
    then cfg.pdAddr
    else if (config.services ? chainfire) && config.services.chainfire.enable
    then
      if config.services.chainfire.apiAddr != null
      then config.services.chainfire.apiAddr
      else "127.0.0.1:${toString config.services.chainfire.port}"
    else "127.0.0.1:2379";
  pdAddrMatch = builtins.match "(.*):([0-9]+)" defaultPdAddr;
  pdHost =
    if pdAddrMatch == null
    then throw "services.flaredb.pdAddr must be host:port (got '${defaultPdAddr}')"
    else builtins.elemAt pdAddrMatch 0;
  pdPort =
    if pdAddrMatch == null
    then throw "services.flaredb.pdAddr must be host:port (got '${defaultPdAddr}')"
    else builtins.elemAt pdAddrMatch 1;
  derivedPdEndpoints = lib.unique ([ defaultPdAddr ] ++ chainfirePeerApiEndpoints);
  localDependencies =
    lib.optionals ((config.services ? chainfire) && config.services.chainfire.enable) [ "chainfire.service" ];
  flaredbConfigFile = tomlFormat.generate "flaredb.toml" (lib.recursiveUpdate {
    addr = apiAddrArg;
    http_addr = "0.0.0.0:${toString cfg.httpPort}";
    data_dir = toString cfg.dataDir;
    pd_addr = defaultPdAddr;
    pd_endpoints = derivedPdEndpoints;
    log_level = "info";
    namespace_modes = {
      default = "strong";
      validation = "eventual";
      plasmavmc = "strong";
      lightningstor = "eventual";
      prismnet = "eventual";
      flashdns = "eventual";
      fiberlb = "eventual";
      creditservice = "strong";
      k8shost = "eventual";
    };
  } cfg.settings);
in
{
  options.services.flaredb = {
    enable = lib.mkEnableOption "flaredb distributed SQL/KV service";

    nodeId = lib.mkOption {
      type = lib.types.str;
      default = config.networking.hostName;
      description = "Unique node identifier for the Raft cluster";
    };

    port = lib.mkOption {
      type = lib.types.port;
      default = 2479;
      description = "Port for flaredb API";
    };

    raftPort = lib.mkOption {
      type = lib.types.port;
      default = 2480;
      description = "Port for flaredb Raft protocol";
    };

    raftAddr = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = "Full address for Raft (host:port). If null, uses 0.0.0.0:raftPort";
    };

    apiAddr = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = "Full address for API (host:port). If null, uses 0.0.0.0:port";
    };

    initialPeers = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = [];
      description = "Initial Raft peers for cluster bootstrap";
      example = [ "node01=10.0.0.1:2480" "node02=10.0.0.2:2480" ];
    };

    httpPort = lib.mkOption {
      type = lib.types.port;
      default = 8082;
      description = "Port for flaredb HTTP/admin API (used for cluster join)";
    };

    pdAddr = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = "ChainFire placement-driver endpoint (host:port). Defaults to the local ChainFire API when enabled.";
      example = "10.0.0.1:2379";
    };

    dataDir = lib.mkOption {
      type = lib.types.path;
      default = "/var/lib/flaredb";
      description = "Data directory for flaredb";
    };

    dbaasEnabled = lib.mkOption {
      type = lib.types.bool;
      default = true;
      description = "Expose FlareDB as the managed DBaaS data plane";
    };

    settings = lib.mkOption {
      type = lib.types.attrs;
      default = {};
      description = "Additional configuration settings";
    };

    package = lib.mkOption {
      type = lib.types.package;
      default = pkgs.flaredb-server or (throw "flaredb-server package not found");
      description = "Package to use for flaredb";
    };
  };

  config = lib.mkIf cfg.enable {
    # Create system user
    users.users.flaredb = {
      isSystemUser = true;
      group = "flaredb";
      description = "FlareDB service user";
      home = cfg.dataDir;
    };

    users.groups.flaredb = {};

    # Create systemd service
    systemd.services.flaredb = {
      description = "FlareDB Distributed Database Service";
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ] ++ localDependencies;
      requires = localDependencies;

      environment = {
        FLAREDB_DBAAS_ENABLED = if cfg.dbaasEnabled then "true" else "false";
      };

      serviceConfig = {
        Type = "simple";
        User = "flaredb";
        Group = "flaredb";
        Restart = "on-failure";
        RestartSec = "10s";

        # State directory management
        StateDirectory = "flaredb";
        StateDirectoryMode = "0750";

        # Security hardening
        NoNewPrivileges = true;
        PrivateTmp = true;
        ProtectSystem = "strict";
        ProtectHome = true;
        ReadWritePaths = [ cfg.dataDir ];

        ExecStartPre = "${pkgs.bash}/bin/bash -lc 'for i in $(seq 1 60); do ${pkgs.netcat}/bin/nc -z ${lib.escapeShellArg pdHost} ${lib.escapeShellArg pdPort} && exit 0; sleep 1; done; echo \"timed out waiting for FlareDB PD ${defaultPdAddr}\" >&2; exit 1'";
        ExecStart = lib.concatStringsSep " " ([
          "${cfg.package}/bin/flaredb-server"
          "--config ${flaredbConfigFile}"
          "--store-id ${numericIdString cfg.nodeId}"
        ] ++ peerArgs);
      };
    };
  };
}
