<?php

namespace App\Controllers;

use App\Classes\Config;
use App\Classes\Http\Http;
use App\Classes\Session;
use App\Model\ConfigModel;

class CacheController extends PageController {

	/**
	 * Maximum amount of files that can be purged on a single request
	 */
	public static int $purgeLimit = 30;

	public function cacheAction(): void
	{
		$config = $this->getConfigValues();

		$this->router->service()->config = $config;
		$this->router->service()->csrfToken = Session::token();
		$this->router->service()->purgeUrl = $this->url . '/purge';
		$this->router->service()->toggleUrl = $this->url . '/toggle';
		parent::view();
	}

	public function purgeAction(): void
	{
		if (!$this->validatePostRequest()) {
			return;
		}

		$token = Config::c('CLOUDFLARE_TOKEN');
		$zone = Config::c('CLOUDFLARE_ZONE');

		$url = "https://api.cloudflare.com/client/v4/zones/$zone/purge_cache";

		if (!_exists($_POST, 'type')) {
			return;
		}

		$bodies = [];
		switch($_POST['type']) {
			case 'css-js':
				$body = self::generateUrls(['css', 'js']);
				$chunks = array_chunk($body['files'], self::$purgeLimit);
				foreach($chunks as $chunk) {
					$bodies[]['files'] = $chunk;
				}
				break;
			case 'fonts-images':
				$body = self::generateUrls(['fonts', 'img', 'media']);
				$chunks = array_chunk($body['files'], self::$purgeLimit);
				foreach($chunks as $chunk) {
					$bodies[]['files'] = $chunk;
				}
				break;
			case 'all':
				$bodies[] = ['purge_everything' => true];
				break;
			default:
				return;
		}

		$response = null;
		foreach($bodies as $body) {
			$response = (new Http)->withToken($token)
								->asJson()
								->acceptJson()
								->post($url, $body);
			if (empty($response->body())
				|| !json_decode($response->body(), true)['success']) {
				break;
			}
		}

		if ($response) {
			echo $response->body();
		}
	}

	public function toggleAction(): void
	{
		if (!$this->validatePostRequest()) {
			return;
		}

		$token = Config::c('CLOUDFLARE_TOKEN');
		$zone = Config::c('CLOUDFLARE_ZONE');

		$url = "https://api.cloudflare.com/client/v4/zones/$zone/settings/development_mode";

		$config = $this->getConfigValues();
		$currentState = $config['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'];

		$response = (new Http)->withToken($token)
							  ->asJson()
							  ->acceptJson()
							  ->patch($url, [
								  'value' => $currentState == '1' ? 'off' : 'on',
							  ]);

		$this->saveConfigValues($response->body());

		echo $response->body();
	}

	//-------------------------------------//

	private function validatePostRequest(): bool
	{
		if ($_SERVER['REQUEST_METHOD'] == 'GET') {
			parent::throw404();
		}

		if (Config::c('CLOUDFLARE_ENABLED') != '1') {
			return false;
		}

		if (!Session::validateToken($_POST)) {
			return false;
		}

		return true;
	}

	private static function generateUrls(array $directories): array
	{
		$result = [];

		foreach ($directories as $directory) {
			$files = array_diff(scandir($directory), ['..', '.']);
			foreach ($files as $file) {
				$result['files'][] = Config::c('APP_URL') . "/$directory/$file";
			}
		}

		return $result;
	}

	private static function getConfigValues(): array
	{
		$result = [];

		$config = [
			'CLOUDFLARE_DEVELOPMENT_MODE_ENABLED' => '0',
			'CLOUDFLARE_DEVELOPMENT_MODE_UPDATED_AT' => '',
			'CLOUDFLARE_DEVELOPMENT_MODE_EXPIRES_IN' => '',
		];

		foreach ($config as $key => $value) {
			$result[$key] = ConfigModel::firstOrCreate(
				['key' => $key],
				['value' => $value]
			)->value;
		}

		if ($result['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED']) {
			$expiresIn = $result['CLOUDFLARE_DEVELOPMENT_MODE_EXPIRES_IN'];
			$updatedAt = $result['CLOUDFLARE_DEVELOPMENT_MODE_UPDATED_AT'];

			$expiresAtObject = new \DateTime($updatedAt);
			$expiresAt = $expiresAtObject
					   ->modify("+ $expiresIn seconds")
					   ->format('Y-m-d H:i:s');
			$nowObject = new \DateTime('now');
			$now = $nowObject->format('Y-m-d H:i:s');

			if ($now >= $expiresAt) {
				ConfigModel::updateOrCreate(
					['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'],
					['value' => 0]);
				$result['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'] = 0;
			}
			else {
				$result['enabled-remaining'] = $expiresAtObject->modify(
					'- ' . $nowObject->getTimestamp() . ' seconds'
				)->format('H:i:s');
			}
		}

		return $result;
	}

	private static function saveConfigValues(string $response): void
	{
		$decodedResponse = json_decode($response, true);
		if ($decodedResponse['success'] == true) {
			$state = $decodedResponse['result']['value'];
			$expiresIn = $decodedResponse['result']['time_remaining'];
			$updatedAt = $decodedResponse['result']['modified_on'];

			$updatedAtFormatted = (new \DateTime($updatedAt))->format('Y-m-d H:i:s');

			ConfigModel::updateOrCreate(
				['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'],
				['value' => $state == 'on' ? 1 : 0]);

			ConfigModel::updateOrCreate(
				['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_EXPIRES_IN'],
				['value' => $expiresIn]);

			ConfigModel::updateOrCreate(
				['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_UPDATED_AT'],
				['value' => $updatedAtFormatted]);
		}
	}

}