<?php

	use Reflect\Rules\Ruleset;
	use Reflect\{Response, Path, Call};

	use VLW\API\Endpoints;
	use VLW\Database\Database;
	use VLW\Database\Tables\About\LanguagesTable;
	use const VLW\{FORGEJO_ENDPOINT_USER, FORGEJO_ENDPOINT_SEARCH};

	require_once Path::root("src/Consts.php");
	require_once Path::root("src/API/Endpoints.php");
	require_once Path::root("src/Database/Tables/About/Languages.php");

	class POST_AboutLanguages extends Database {
		protected readonly Ruleset $ruleset;

		// Tally of all languages used in all configured repositories
		private array $languages = [];

		public function __construct() {
			$this->ruleset = new Ruleset(strict: true);
			$this->ruleset->validate_or_exit();

			parent::__construct();
		}

		// Fetch JSON from URL
		private static function fetch_json(string $url): array {
			return json_decode(file_get_contents($url), true);
		}

		// Fetch JSON from a Forgejo endpoint
		private static function fetch_endpoint(string $endpoint): array {
			$url = $_ENV["server_forgejo"]["base_url"] . $endpoint;
			return self::fetch_json($url);
		}

		// Write $this->languages to a JSON file
		private function cache_languages(): void {
			// Delete existing cache
			(new Call(Endpoints::ABOUT_LANGUAGES->value))->delete();

			$this->db->for(LanguagesTable::NAME);

			foreach ($this->languages as $language => $bytes) {
				$this->db->insert([
					LanguagesTable::ID->value    => $language,
					LanguagesTable::BYTES->value => $bytes
				]);
			}
		}

		// Fetch and add languages to total from a fully-qualified Forgejo URL
		private function add_repository_languages(string $url): void {
			foreach(self::fetch_json($url) as $language => $bytes) {
				// Create key for language if it doesn't exist
				if (!array_key_exists($language, $this->languages)) {
					$this->languages[$language] = 0;
				}

				// Add bytes to language in total
				$this->languages[$language] += $bytes;
			}
		}

		// Tally languages from public repositories for user id
		private function add_public_repositores(int $uid): bool {
			$resp = self::fetch_endpoint(sprintf(FORGEJO_ENDPOINT_SEARCH, $uid));

			// Bail out if request failed or if response indicated a problem
			if (!$resp or $resp["ok"] === false) {
				return false;
			}

			// Add langauges for each public repository
			foreach ($resp["data"] as $repo) {
				$this->add_repository_languages($repo["languages_url"]);
			}

			return true;
		}

		// Add languages from all public repositories for profiles in config
		private function add_repositories_from_config_profiles(): void {
			foreach(explode(",", $_ENV["server_forgejo"]["scan_profiles"]) as $profile) {
				// Resolve user data from username
				$user = self::fetch_endpoint(sprintf(FORGEJO_ENDPOINT_USER, $profile));
				$this->add_public_repositores($user["id"]);
			}
		}

		public function main(): Response {
			$this->add_repositories_from_config_profiles();

			// Sort langauges bytes tally by largest in descending order
			arsort($this->languages);

			$this->cache_languages();
			return new Response($this->languages);
		}
	}