{ config, lib, pkgs, ... }:
with lib;
let
  cfg = config.services.tt-rss;
  configVersion = 26;
  cacheDir = "cache";
  lockDir = "lock";
  feedIconsDir = "feed-icons";
  dbPort = if cfg.database.port == null
    then (if cfg.database.type == "pgsql" then 5432 else 3306)
    else cfg.database.port;
  poolName = "tt-rss";
  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
  tt-rss-config = pkgs.writeText "config.php" ''
    plugins.local directory.
        '';
      };
      themePackages = mkOption {
        type = types.listOf types.package;
        default = [];
        description = ''
          List of themes to install. The list elements are expected to
          be derivations. All elements in this derivation are automatically
          copied to the themes.local directory.
        '';
      };
      logDestination = mkOption {
        type = types.enum ["" "sql" "syslog"];
        default = "sql";
        description = ''
          Log destination to use. Possible values: sql (uses internal logging
          you can read in Preferences -> System), syslog - logs to system log.
          Setting this to blank uses PHP logging (usually to http server
          error.log).
        '';
      };
      extraConfig = mkOption {
        type = types.lines;
        default = "";
        description = ''
          Additional lines to append to config.php.
        '';
      };
    };
  };
  imports = [
    (mkRemovedOptionModule ["services" "tt-rss" "checkForUpdates"] ''
      This option was removed because setting this to true will cause TT-RSS
      to be unable to start if an automatic update of the code in
      services.tt-rss.root leads to a database schema upgrade that is not
      supported by the code active in the Nix store.
    '')
  ];
  ###### implementation
  config = mkIf cfg.enable {
    assertions = [
      {
        assertion = cfg.database.password != null -> cfg.database.passwordFile == null;
        message = "Cannot set both password and passwordFile";
      }
    ];
    services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
      ${poolName} = {
        inherit (cfg) user;
        settings = mapAttrs (name: mkDefault) {
          "listen.owner" = "nginx";
          "listen.group" = "nginx";
          "listen.mode" = "0600";
          "pm" = "dynamic";
          "pm.max_children" = 75;
          "pm.start_servers" = 10;
          "pm.min_spare_servers" = 5;
          "pm.max_spare_servers" = 20;
          "pm.max_requests" = 500;
          "catch_workers_output" = 1;
        };
      };
    };
    # NOTE: No configuration is done if not using virtual host
    services.nginx = mkIf (cfg.virtualHost != null) {
      enable = true;
      virtualHosts = {
        ${cfg.virtualHost} = {
          root = "${cfg.root}";
          locations."/" = {
            index = "index.php";
          };
          locations."~ \\.php$" = {
            extraConfig = ''
              fastcgi_split_path_info ^(.+\.php)(/.+)$;
              fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.socket};
              fastcgi_index index.php;
            '';
          };
        };
      };
    };
    systemd.tmpfiles.rules = [
      "d '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
      "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
    ];
    systemd.services.tt-rss =
      {
        description = "Tiny Tiny RSS feeds update daemon";
        preStart = let
          callSql = e:
              if cfg.database.type == "pgsql" then ''
                  ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \
                  ${optionalString (cfg.database.passwordFile != null) "PGPASSWORD=$(cat ${cfg.database.passwordFile})"} \
                  ${config.services.postgresql.package}/bin/psql \
                    -U ${cfg.database.user} \
                    ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \
                    -c '${e}' \
                    ${cfg.database.name}''
              else if cfg.database.type == "mysql" then ''
                  echo '${e}' | ${config.services.mysql.package}/bin/mysql \
                    -u ${cfg.database.user} \
                    ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
                    ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
                    ${cfg.database.name}''
              else "";
        in ''
          rm -rf "${cfg.root}/*"
          cp -r "${pkgs.tt-rss}/"* "${cfg.root}"
          ${optionalString (cfg.pluginPackages != []) ''
            for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
              cp -r "$plugin"/* "${cfg.root}/plugins.local/"
            done
          ''}
          ${optionalString (cfg.themePackages != []) ''
            for theme in ${concatStringsSep " " cfg.themePackages}; do
              cp -r "$theme"/* "${cfg.root}/themes.local/"
            done
          ''}
          ln -sf "${tt-rss-config}" "${cfg.root}/config.php"
          chmod -R 755 "${cfg.root}"
        ''
        + (optionalString (cfg.database.type == "pgsql") ''
          exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
          | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
          if [ "$exists" == 'f' ]; then
            ${callSql "\\i ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"}
          else
            echo 'The database contains some data. Leaving it as it is.'
          fi;
        '')
        + (optionalString (cfg.database.type == "mysql") ''
          exists=$(${callSql "select count(*) > 0 from information_schema.tables where table_schema = schema()"} \
          | tail -n+2 | sed -e 's/[ \n\t]*//')
          if [ "$exists" == '0' ]; then
            ${callSql "\\. ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"}
          else
            echo 'The database contains some data. Leaving it as it is.'
          fi;
        '');
        serviceConfig = {
          User = "${cfg.user}";
          Group = "tt_rss";
          ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon";
          StandardOutput = "syslog";
          StandardError = "syslog";
        };
        wantedBy = [ "multi-user.target" ];
        requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
        after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
    };
    services.mysql = mkIf mysqlLocal {
      enable = true;
      package = mkDefault pkgs.mysql;
      ensureDatabases = [ cfg.database.name ];
      ensureUsers = [
        {
          name = cfg.user;
          ensurePermissions = {
            "${cfg.database.name}.*" = "ALL PRIVILEGES";
          };
        }
      ];
    };
    services.postgresql = mkIf pgsqlLocal {
      enable = mkDefault true;
      ensureDatabases = [ cfg.database.name ];
      ensureUsers = [
        { name = cfg.user;
          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
        }
      ];
    };
    users.users.tt_rss = optionalAttrs (cfg.user == "tt_rss") {
      description = "tt-rss service user";
      isSystemUser = true;
      group = "tt_rss";
    };
    users.groups.tt_rss = {};
  };
}