<?php

namespace SHServ\Models;

use \SHServ\Tools\DeviceScanner;
use \SHServ\Tools\DeviceAPI\Base;
use \SHServ\Entities\Device;
use \SHServ\Entities\DeviceAuth;

class Devices extends \SHServ\Middleware\Model {
	public function connect_new_device(String $device_ip, String $alias = "", String $name = "", String $description = ""): Device | Array {
		// validate device

		$device_api = new Base($device_ip);
		$device_info = $device_api -> get_about();
		if(!$device_info["data"]) {
			return [
				"result" => false,
				"err_alias" => "device_not_found"
			];
		}

		if($device_info["data"]["status"] != "setup") {
			return [
				"result" => false,
				"err_alias" => "device_mode_error"
			];
		}

		// create in table devices

		$device_id = app() -> thin_builder -> insert(Device::$table_name, [
			"alias" => $alias,
			"name" => $name,
			"device_type" => $device_info["data"]["device_type"],
			"device_ip" => $device_info["data"]["ip_address"],
			"device_mac" => $device_info["data"]["mac_address"],
			"device_hard_id" => $device_info["data"]["device_id"],
			"firmware_version" => $device_info["data"]["firmware_version"],
			"connection_state" => "active",
			"state" => "active",
			"description" => $description,
			"last_contact" => date("Y-m-d H:i:s"),
			"create_at" => date("Y-m-d H:i:s")
		]);

		$device = $device_id ? new Device($device_id) : null;

		if(!$device) {
			return [
				"result" => false,
				"err_alias" => "db_error"
			];
		}

		// generate token
		
		$device_token = app() -> utils -> generate_token(16); 
		
		// create in table device_auth
		
		$device_auth = app() -> thin_builder -> insert(DeviceAuth::$table_name, [
			"device_id" => $device -> id(),
			"device_token" => $device_token,
			"state" => "active",
			"create_at" => date("Y-m-d H:i:s")
		]);

		if(!$device_auth) {
			app() -> thin_builder -> delete(Device::$table_name, [ "id", "=", $device -> id() ]);

			return [
				"result" => false,
				"err_alias" => "device_error_of_auth"
			];
		}

		$device -> set_device_token($device_token);
		$device -> device_api() -> set_device_name($name);

		return $device;
	}

	public function remove_device(int $device_id): Array {
		$device = $this -> by_id($device_id);

		if(!$device) {
			return [
				"result" => false,
				"err_alias" => "device_not_found"
			];
		}
		
		$device_auth = $device -> auth();
		if(!$device_auth) {
			return [
				"result" => false,
				"err_alias" => "device_not_found"
			];
		}

		$response = $device -> device_api() -> reset();
		if($response["http_code"] != 200) {
			return [
				"result" => false,
				"err_alias" => "device_request_fail"
			];
		}

		if($response["data"]["status"] == "ok") {
			// state killed in device_auth table
			$device_auth -> kill();

			// remove from devices
			$device -> remove();

			return [
				"result" => true,
				"device_msg" => $response["data"]["message"] ?? ""
			];
		}

		$err_type = $response["data"]["error"] ?? "undefined_error";
		$err_msg = $response["data"]["message"] ?? "Unknown error";

		return [
			"result" => false,
			"err_alias" => "device_request_fail",
			"device_msg" => "<b>{$err_type}</b>. {$err_msg}"
		];
	}

	public function get_device_list(String $state = "active"): Array {
		// get from db table devices with state = "active"
		
		$raw_devices = app() -> thin_builder -> select(
			Device::$table_name, 
			Device::get_fields(), 
			[ ["state", "=", $state] ],
			[ "id" ],
			"DESC"
		);

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

		// wrapped result to Device entity
		
		$devices = [];
		foreach($raw_devices as $i => $raw_device) {
			$devices[] = new Device($raw_device["id"], $raw_device);
		}

		return $devices;
	}

	public function get_unregistered_devices(): Array {
		return $this -> scanning_localnet(FCONF["device_ip_range"][0], FCONF["device_ip_range"][1], "setup");
	}

	public function scanning_localnet(String $from_ip, String $to_ip, String $status = ""): Array {
		$device_scanner = new DeviceScanner();
		$devices = $device_scanner -> scan_range($from_ip, $to_ip);
		if(!$status) {
			return $devices;
		}

		return $device_scanner -> filter_by_status($devices, $status);
	}

	public function by_hard_id(String $device_hard_id): Device | null {
		$results = app() -> thin_builder -> select(
			Device::$table_name,
			Device::get_fields(),
			[
				[ "device_hard_id", "=", $device_hard_id ],
				"AND",
				[ "state", "=", "active" ]
			]
		);

		if(!$results) {
			return null;
		}

		return new Device($results[0]["id"], $results[0]);
	}

	public function by_alias(String $alias): Device | null {
		$results = app() -> thin_builder -> select(
			Device::$table_name,
			Device::get_fields(),
			[
				[ "alias", "=", $alias ],
				"AND",
				[ "state", "=", "active" ]
			]
		);

		if(!$results) {
			return null;
		}

		return new Device($results[0]["id"], $results[0]);
	}

	public function by_id(int $device_id): Device | null {
		$results = app() -> thin_builder -> select(
			Device::$table_name,
			Device::get_fields(),
			[
				[ "id", "=", $device_id ],
				"AND",
				[ "state", "=", "active" ]
			]
		);

		if(!$results) {
			return null;
		}

		return new Device($results[0]["id"], $results[0]);
	}

	public function reboot_device(int $device_id): Array {
		$device = $this -> by_id($device_id);

		if(!$device) {
			return [
				"result" => false,
				"err_alias" => "device_not_found"
			];
		}

		$response = $device -> device_api() -> reboot();
		if($response["http_code"] != 200) {
			return [
				"result" => false,
				"err_alias" => "device_request_fail"
			];
		}

		if($response["data"]["status"] == "ok") {
			return [
				"result" => true,
				"msg" => $response["data"]["message"] ?? ""
			];
		}

		$err_type = $response["data"]["error"] ?? "undefined_error";
		$err_msg = $response["data"]["message"] ?? "Unknown error";

		return [
			"result" => false,
			"err_alias" => "device_request_fail",
			"msg" => "<b>{$err_type}</b>. {$err_msg}"
		];
	}

	public function get_device_info($device_id_or_alias): Array | null {
		if(is_string($device_id_or_alias)) {
			$device = $this -> by_alias($device_id_or_alias);
		} else {
			$device = $this -> by_id($device_id_or_alias);
		}

		if(!$device) {
			return null;
		}

		$about = $device -> device_api() -> get_about();
		$info = [
			"id" => $device -> id(),
			"alias" => $device -> alias,
			"name" => $device -> name,
			"description" => $device -> description,
			"state" => $device -> state,
			"connection_state" => $device -> connection_state,
			"last_contact" => $device -> last_contact,
			"create_at" => $device -> create_at,
			"device" => $about["data"] ?? []
		];

		return $info;
	}

	public function alias_is_uniq(String $alias): bool {
		$count = app() -> thin_builder -> count(
			Device::$table_name, 
			[ ["alias", "=", $alias] ]
		);

		return $count ? false : true;
	}
}