let
	nixpkgsOptions = {
		overlays = [
			(self: super: {
				/* NOTE: Namespaced under `pkgs.cryto.*` to prevent naming conflicts with upstream nixpkgs */
				cryto = {
					fetchFromCrytoGit = self.callPackage ./lib/fetch/from-cryto-git.nix {};
					nodeApplication = self.callPackage ./lib/node-application.nix {};
					unpack = self.callPackage ./lib/unpack.nix {};
					mobileProxy = self.callPackage ./packages/mobile-proxy { configFile = null; };
				};
			})
		];
	};
	pkgs = (import (fetchTarball "https://github.com/NixOS/nixpkgs-channels/archive/nixos-19.03.tar.gz") nixpkgsOptions);
	pkgs1803 = (import (fetchTarball "https://github.com/NixOS/nixpkgs-channels/archive/nixos-18.03.tar.gz") nixpkgsOptions);
	presets = {
		base = (import ./presets/base.nix);
		kvm = (import ./presets/kvm.nix);
	};
	nginxPresets = {
		php = (import ./presets/nginx/php.nix);
		cphpApplication = (import ./presets/nginx/cphp-application.nix);
		reverseProxy = (import ./presets/nginx/reverse-proxy.nix);
		letsEncrypt = (import ./presets/nginx/lets-encrypt.nix);
	};
in {
	network = {
		inherit pkgs;
		description = "Cryto";
	};

	"machine-borg2-01.cryto.net" = { pkgs, lib, ... }: {
		system.stateVersion = "18.09";

		imports = [
			presets.base
			presets.kvm
			./hardware-configurations/machine-borg2-01.nix
		];

		boot.loader.grub.device = lib.mkForce "/dev/vda";

		users.extraUsers = {
			backup-f0x = {
				createHome = true;
				home = "/home/backup-f0x";
			};
		};

		users.extraGroups = {
			backup-f0x = { members = [ "backup-f0x" ]; };
		};

		services.borgbackup.repos = {
			"f0x" = {
				allowSubRepos = true;
				quota = "250G";
				path = "/home/backup-f0x";
				user = "backup-f0x";
				group = "backup-f0x";
				authorizedKeys = [
					"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjJDP2TDyj1X/L6gNgHCXASIWoW/VnJ77FQy39VRTi8 f0x@elephantus"
				];
				authorizedKeysAppendOnly = [
					"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG7WSUY6Y2lsIawo8dPBu4/Omx6c7/1SMD9ve/vpcorN borg-backup@terra"
					"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeMWPR38zXAbURVTJs+yGDnld5kO7bcgp/70l4wJG0k borg-backup@luna"
				];
			};
		};
	};

	"machine-haless-03.cryto.net" = { pkgs, lib, config, ... }@args: {
		system.stateVersion = "19.03";

		imports = [
			presets.base
			presets.kvm
			./hardware-configurations/machine-haless-03.nix
		];

		deployment.healthChecks.http = let
			makeHostChecker = { protocol, port }: host: {
				scheme = protocol;
				port = port;
				path = "/";
				host = host;
				description = "${host} (${protocol} :${toString port}) is up";
			};
			httpHosts = hosts: map (makeHostChecker { protocol = "http"; port = 80; }) hosts;
			httpsHosts = hosts: map (makeHostChecker { protocol = "https"; port = 443; }) hosts;
		in lib.mkMerge [
			(httpHosts [
				# "haless.cryto.net"
				"todo.cryto.net"
				"books.cryto.net"
				"learn.cryto.net"
				"vps-list.cryto.net"
				"iomfats.cryto.net"
				"castleroland.cryto.net"
				"awesomedude.cryto.net"
			])
			(httpsHosts [
				# "haless.cryto.net"
				"books.cryto.net"
				"vps-list.cryto.net"
				"iomfats.cryto.net"
				"castleroland.cryto.net"
				"awesomedude.cryto.net"
			])
		];

		networking.firewall.allowedTCPPorts = [ 80 443 ];

		services.nginx = {
			enable = true;
			virtualHosts = {
				"404.cryto.net" = {
					default = true;
					extraConfig = ''
						return 404;
					'';
				};
				"haless.cryto.net" = lib.mkMerge [
					(nginxPresets.letsEncrypt)
					{
						locations."/shadow" = {
							alias = ./sources/shadow-generator;
						};
						locations."/knex-mirror" = {
							alias = ./sources/knex-mirror;
						};
					}
				];
				"books.cryto.net" = lib.mkMerge [
					(nginxPresets.letsEncrypt)
					(nginxPresets.php args) /* Temporary hack until I can figure out the mkMerge evaluation order issue */
					{
						root = pkgs.stdenv.mkDerivation {
							name = "cryto-books";
							src = ./sources/cryto-books;

							installPhase = ''
								mkdir -p $out/
								cp -r $src/* $out/
								cp ${../private/cryto-books/credentials.php} $out/credentials.php
							'';
						};
					}
				];
				"todo.cryto.net" = lib.mkMerge [
					(nginxPresets.php args) /* Temporary hack until I can figure out the mkMerge evaluation order issue */
					(nginxPresets.cphpApplication (pkgs.stdenv.mkDerivation {
						name = "cryto-todo";
						src = ./sources/cryto-todo;

						installPhase = ''
							mkdir -p $out/public_html
							cp -r $src/* $out/public_html
							cp ${../private/cryto-todo/config.json} $out/config.json
						'';
					}))
				];
				"learn.cryto.net" = lib.mkMerge [
					(nginxPresets.php args) /* Temporary hack until I can figure out the mkMerge evaluation order issue */
					(nginxPresets.cphpApplication (pkgs.stdenv.mkDerivation {
						name = "cryto-learn";
						src = ./sources/cryto-learn;

						installPhase = ''
							mkdir -p $out/public_html
							cp -r $src/* $out/public_html
							cp ${../private/cryto-learn/config.json} $out/config.json
						'';
					}))
				];
				"vps-list.cryto.net" = lib.mkMerge [
					(nginxPresets.letsEncrypt)
					(nginxPresets.php args) /* Temporary hack until I can figure out the mkMerge evaluation order issue */
					(nginxPresets.cphpApplication (pkgs.stdenv.mkDerivation {
						name = "vps-list";
						src = ./sources/vps-list;

						installPhase = ''
							mkdir -p $out/public_html
							mkdir -p $out/public_html/cphp

							cp -r $src/* $out/public_html
							cp ${../private/vps-list/config.php} $out/public_html/cphp/config.php
						'';
					}))
				];
				"iomfats.cryto.net" = lib.mkMerge [
					(nginxPresets.letsEncrypt)
					(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
				];
				"castleroland.cryto.net" = lib.mkMerge [
					(nginxPresets.letsEncrypt)
					(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
				];
				"awesomedude.cryto.net" = lib.mkMerge [
					(nginxPresets.letsEncrypt)
					(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
				];
			};
		};

		services.mysql = {
			enable = true;
			package = pkgs.mysql55;
		};

		services.phpfpm = {
			extraConfig = ''
				log_level = notice
			'';
			phpPackage = pkgs1803.php56;
			pools = {
				main = {
					listen = "/var/run/phpfpm-main.sock";
					extraConfig = ''
						user = nobody
						listen.owner = nginx
						listen.group = nginx
						listen.mode = 0660

						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 = yes
					'';
				};
			};
		};

		users.extraUsers.mobile-proxy = {
			description = "mobile-proxy Service User";
		};

		systemd.services.mobile-proxy = let
			package = pkgs.cryto.mobileProxy.override { configFile = ./data/mobile-proxy/config.jsx; };
		in {
			description = "Mobile Proxy";
			wantedBy = ["multi-user.target"];
			after = ["network.target"];

			serviceConfig = {
				ExecStart = "${package}/bin/mobile-proxy";
				User = "mobile-proxy";
				Restart = "on-failure";
				PermissionsStartOnly = true;
			};

			preStart = ''
				mkdir -m 0700 -p /tmp/mobile-proxy-home
				chown mobile-proxy /tmp/mobile-proxy-home
			'';

			environment =  {
				HOME = "/tmp/mobile-proxy-home";
			};
		};
	};
}
