<?php

namespace SHServ\Entities;

use \SHServ\Entities\Device;
use \SHServ\Entities\Script;

class Area extends \SHServ\Middleware\Entity {

	use \SHServ\Helpers\MetaImplementation;

	public static $table_name = "areas";
	protected static $fields = [
		"id", "alias", "display_name", "type", 
		"parent_id", "schema", "create_at", "update_at"
	];

	public function __construct(int $entity_id, array $data = []) {
		return parent::__construct(
			self::$table_name, 
			$entity_id, 
			$data
		);
	}

	public function get_parent_id(): Int {
		return $this -> parent_id; 
	}

	public function parent_area(): Area | null {
		if(!$this -> get_parent_id()) {
			return null;
		}

		return new Area($this -> get_parent_id());
	}

	public function get_inner_areas(Bool $recursive = false): Array {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}

		$raw_areas = app() -> thin_builder -> select(
			self::$table_name,
			self::get_fields(),
			[[ "parent_id", "IN", $area_ids ]]
		);

		if(!$raw_areas) {
			return [];
		}

		$areas = [];

		foreach($raw_areas as $i => $raw_area) {
			$areas[] = new Area($raw_area["id"], $raw_area);
		}

		return $areas;
	}

	public function place_in_area(Area $parent_area): Array | bool {
		return $this -> place_in_area_id($parent_area -> id());
	}

	public function place_in_area_id(int $area_id): Array | bool {
		$this -> parent_id = $area_id;
		return $this -> update();
	}

	public function get_areas_by_type(String $type, Bool $recursive = false): Array {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}

		$raw_areas = app() -> thin_builder -> select(
			self::$table_name,
			self::get_fields(),
			[
				[ "parent_id", "IN", $area_ids ], 
				"AND", 
				[ "type", "=", $type ]
			]
		);

		if(!$raw_areas) {
			return [];
		}

		$areas = [];

		foreach($raw_areas as $i => $raw_area) {
			$areas[] = new Area($raw_area["id"], $raw_area);
		}

		return $areas;
	}

	public function get_area_by_alias(String $alias, Bool $recursive = false): Area | null {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}

		$raw_areas = app() -> thin_builder -> select(
			self::$table_name,
			self::get_fields(),
			[
				[ "parent_id", "IN", $area_ids], 
				"AND", 
				[ "alias", "=", $alias ]
			],
			[], "DESC",
			[ 1 ]
		);

		if(!$raw_areas) {
			return null;
		}

		return new Area($raw_areas[0]["id"], $raw_areas[0]);
	}

	protected function get_recursive_inner_areas_ids(Int $parent_id, Int $lvl = 0) {
		if($lvl >= 10) {
			return [];
		}

		$lvl++;

		$areas = app() -> thin_builder -> select(
			Area::$table_name,
			["id", "parent_id"],
			[["parent_id", "=", $parent_id]]
		);

		$result = [];

		if(!$areas) {
			return $result;
		}

		foreach($areas as $area) {
			$result[] = $area["id"];
			$result_next_lvl = $this -> get_recursive_inner_areas_ids($area["id"], $lvl);

			if($result_next_lvl) {
				$result = array_merge($result, $result_next_lvl);
			}
		}

		return $result;
	}

	public function get_inner_devices(Bool $recursive = false): Array {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}
		
		$raw_devices = app() -> thin_builder -> select(
			Device::$table_name,
			Device::get_fields(),
			[["area_id", "IN", $area_ids]]
		);

		if(!$raw_devices) {
			return [];
		}

		$devices = [];
		foreach($raw_devices as $raw_device) {
			$devices[] = new Device($raw_device["id"], $raw_device);
		}

		return $devices;
	}

	public function get_inner_scripts(Bool $recursive = false): Array {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}
		
		$raw_scripts = app() -> thin_builder -> select(
			Script::$table_name,
			Script::get_fields(),
			[["area_id", "IN", $area_ids]],
			["id"], "DESC"
		);

		if(!$raw_scripts) {
			return [];
		}

		$scripts = [];
		foreach($raw_scripts as $raw_script) {
			$scripts[] = new Script($raw_script["id"], $raw_script);
		}

		return $scripts;
	}

	public function get_device_by_alias(String $alias, Bool $recursive = false): Device | null {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}

		$raw_devices = app() -> thin_builder -> select(
			Device::$table_name,
			Device::get_fields(),
			[
				[ "area_id", "IN", $area_ids ],
				"AND", 
				[ "alias", "=", $alias ]
			],
			[], "DESC", 
			[ 1 ]
		);

		if(!$raw_devices) {
			return null;
		}

		$raw_device = $raw_devices[0];
		return new Device($raw_device["id"], $raw_device);
	}

	public function get_devices_by_type(String $type, Bool $recursive = false): Array {
		$area_ids = [ $this -> id() ];
		
		if($recursive) {
			$area_ids = array_merge($area_ids, $this -> get_recursive_inner_areas_ids($this -> id()));
		}

		$raw_devices = app() -> thin_builder -> select(
			Device::$table_name,
			Device::get_fields(),
			[
				[ "area_id", "IN", $area_ids ],
				"AND",
				[ "device_type", "=", $type ] 
			]
		);

		if(!$raw_devices) {
			return [];
		}

		$devices = [];
		foreach($raw_devices as $raw_device) {
			$devices[] = new Device($raw_device["id"], $raw_device);
		}

		return $devices;
	}

	public function remove(): Bool {
		app() -> thin_builder -> beginTransaction();
		try {
			app() -> thin_builder -> update(
				Device::$table_name,
				["area_id" => 0],
				[[ "area_id", "=", $this -> id() ]]
			);

			app() -> thin_builder -> update(
				self::$table_name,
				["parent_id" => 0],
				[[ "parent_id", "=", $this -> id() ]]
			);

			$this -> remove_entity();
			app() -> thin_builder -> commit();
			return true;
		} catch (\Exception $e) {
			app() -> thin_builder -> rollBack();
			throw $e;
		}
	}
}