<?php

namespace SHServ\Middleware;

use \SHServ\Models\Devices;
use \SHServ\Models\Scripts;
use \SHServ\Helpers\DeviceScriptsHelper;

abstract class ControlScripts {
	protected $devices_model;
	protected $device_scripts_helper;
	protected static $regular_scripts = [];
	protected static $actions_scripts = [];

	abstract protected function register_events_handlers(): void;
	abstract protected function register_regular_scripts(): void;
	abstract protected function register_actions_scripts(): void;

	public function __construct() {
		list($scope_folder, $scope_name) = explode("\\", str_replace("SHServ", "", static::class));
		
		if($scope_folder != "ControlScripts" or (new Scripts()) -> script_state("scope", $scope_name)) {
			$this -> register_events_handlers();
			$this -> register_regular_scripts();
			$this -> register_actions_scripts();
		}
	}

	protected function add_event_handler(String $event_name, callable $handler): void {
		events() -> handler("app:{$event_name}", function(Array $params) use ($handler) {
			$handler($params["device"], $params["data"]);
		});
	}

	protected function devices(): Devices {
		if(!$this -> devices_model) {
			$this -> devices_model = new Devices();
		}

		return $this -> devices_model;
	}

	protected function helper(): DeviceScriptsHelper {
		if(!$this -> device_scripts_helper) {
			$this -> device_scripts_helper = new DeviceScriptsHelper($this -> devices());
		}

		return $this -> device_scripts_helper;
	}

	protected function add_regular_script(Array $attributes, callable $script): bool {
		if(!isset($attributes["alias"])) {
			return false;
		}

		if(isset(self::$regular_scripts[$attributes["alias"]])) {
			return false;
		}

		$ref = new \ReflectionClass(static::class);
		$path_info = pathinfo($ref -> getFileName());

		$attributes["name"] = $attributes["name"] ?? "unknown";
		$attributes["description"] = $attributes["description"] ?? "";
		$attributes["classname"] = static::class;
		$attributes["path"] = $path_info["dirname"];
		$attributes["filename"] = $path_info["basename"];
		$attributes["author"] = $attributes["author"] ?? "Unknown author";

		self::$regular_scripts[$attributes["alias"]] = [
			"attributes" => $attributes,
			"code" => $this -> get_source_code($script),
			"script" => $script
		];

		return true;
	}

	public static function get_regular_scripts(): Array {
		return self::$regular_scripts;
	}

	public static function get_actions_scripts(): Array {
		return self::$actions_scripts;
	}

	public function add_action_script(Array $attributes, callable $script): bool {
		if(!isset($attributes["alias"])) {
			return false;
		}

		if(isset(self::$actions_scripts[$attributes["alias"]])) {
			return false;
		}

		$ref = new \ReflectionClass(static::class);
		$path_info = pathinfo($ref -> getFileName());

		$attributes["name"] = $attributes["name"] ?? "unknown";
		$attributes["description"] = $attributes["description"] ?? "";
		$attributes["classname"] = static::class;
		$attributes["path"] = $path_info["dirname"];
		$attributes["filename"] = $path_info["basename"];
		$attributes["author"] = $attributes["author"] ?? "Unknown author";

		self::$actions_scripts[$attributes["alias"]] = [
			"attributes" => $attributes,
			"code" => $this -> get_source_code($script),
			"script" => $script
		];

		return true;
	}

	public static function run_action_script(String $alias, Array $params): Array | null {
		if(!isset(self::$actions_scripts[$alias])) {
			return null;
		}

		$scripts_model = new Scripts();
		if(!$scripts_model -> script_state("action", $alias)) {
			return null;
		}

		$start_time = microtime(true);

		$result = self::$actions_scripts[$alias]["script"]($params);
		
		$exec_time = microtime(true) - $start_time;
		$exec_time = round($exec_time, 3);

		return [
			"result" => $result,
			"exec_time" => "{$exec_time} seconds"
		];
	}

	protected function get_source_code($func): String {
		$ref_func = new \ReflectionFunction($func);
		$file_name = $ref_func -> getFileName();
		$start_line = $ref_func -> getStartLine();
		$end_line = $ref_func -> getEndLine();
		$lines = file($file_name);

		$code = implode(
			"",
			array_slice(
				$lines,
				$start_line - 1,
				$end_line - $start_line + 1
			)
		);

		return $code;
	}
}