<?php

namespace SHServ\Helpers;

use \SHServ\Models\Devices;

class DeviceScriptsHelper {
	protected $devices_model;

	public function __construct(Devices $devices_model) {
		$this -> devices_model = $devices_model;
	}

	public function devices(): Devices {
		return $this -> devices_model;
	}


	/**
	 * Синхронизировать указанный канал реле с указанным каналом устройства кнопки.
	 * Синхронизация низкоуровневая и одностороняя, от реле к кнопке.
	 * @param  Relay       $relay  
	 * @param  Button      $btn
	 * @param  int|integer $relay_channel
	 * @param  int|integer $btn_channel
	 * @return void
	 */
	public function sync_relay_to_btn_channel(\SHServ\Tools\DeviceAPI\Relay $relay_api, \SHServ\Tools\DeviceAPI\Button $btn_block_api, int $relay_channel = 0, int $btn_channel = 0): void {
		$relay_channels = ($relay_api -> get_status())["channels"] ?? [];

		if(!isset($relay_channels[$relay_channel]["state"])) {
			return;
		}

		$btn_block_api -> set_channel_state(
			$relay_channels[$relay_channel]["state"] == "on" ? "enabled" : "disabled", 
			$btn_channel
		);
	}

	/**
	 * Синхронизировать состояние каналов реле с соответствующими блоками кнопок с указанными каналами. 
	 * Синхронизация односторонняя от реле к кнопкам
	 * @param  Array  $sync_map    [ 
	 *   relay_channel_num => [
	 *     ["alias" => "button_alias", "channel" => 1],
	 *     ["alias" => "button_alias_2", "channel" => 0],
	 *   ]
	 * ]
	 * @param  String $relay_alias 
	 * @return Bool
	 */
	public function sync_relay_to_btns(Array $sync_map, String $relay_alias): Bool {
		if(isset($sync_map["connections"])) {
			$sync_map = $this -> prepare_sync_map_by_alias($sync_map, $relay_alias);
		}

		$relay = $this -> devices() -> by_alias($relay_alias);
		if(!$relay) {
			return false;
		}

		$relay_api = $relay -> device_api();
		if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
			return false;
		}

		foreach($sync_map as $relay_channel => $btns) {
			foreach($btns as $item) {
				$btn = $this -> devices() -> by_alias($item["alias"]);
				if(!$btn) {
					continue;
				}

				$btn_api = $btn -> device_api();
				if(!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) {
					continue;
				}

				$this -> sync_relay_to_btn_channel($relay_api, $btn_api, $relay_channel, intval($item["channel"]));
			}
		}

		return true;
	}

	/**
	 * Синхронизировать состояние каналов устройства "кнопка" с соответствующими реле с указанными каналами. 
	 * Синхронизация односторонняя от реле к кнопкам
	 * @param  Array  $sync_map    [ 
	 *   button_channel_num => [
	 *     ["alias" => "relay_alias", "channel" => 1],
	 *     ["alias" => "relay_alias_2", "channel" => 0],
	 *   ]
	 * ]
	 * @param  String $btn_alias 
	 * @return Bool
	 */
	public function sync_btn_channels(Array $sync_map, String $btn_alias): Bool {
		if(isset($sync_map["connections"])) {
			$sync_map = $this -> prepare_sync_map_by_alias($sync_map, $btn_alias);
		}

		$btn = $this -> devices() -> by_alias($btn_alias);
		if(!$btn) {
			return false;
		}

		$btn_api = $btn -> device_api();
		if(!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) {
			return false;
		}

		foreach($sync_map as $btn_channel => $relays) {
			foreach($relays as $item) {
				$relay = $this -> devices() -> by_alias($item["alias"]);
				if(!$relay) {
					continue;
				}

				$relay_api = $relay -> device_api();
				if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
					continue;
				}

				$this -> sync_relay_to_btn_channel($relay_api, $btn_api, intval($item["channel"]), $btn_channel);
			}
		}

		return true;
	}

	public function get_sync_entries_by_type(Array $sync_map, String $type): Array {
		$map = $sync_map["connections"];
		$device_entries = [];

		foreach($map as $connection) {
			foreach($connection as $device_entry) {
				if(!isset($device_entry["type"]) or $device_entry["type"] != $type) {
					continue;
				}

				if(!isset($device_entry["alias"])) {
					continue;
				}

				$device_entries[] = $device_entry;
			}
		}

		return $device_entries;
	}

	public function prepare_sync_map_by_alias(Array $sync_map, String $alias): Array {
		$result = [];

		if(!isset($sync_map["connections"])) {
			return $result;
		}

		foreach($sync_map["connections"] as $connection) {
			foreach($connection as $device_entry) {
				if($device_entry["alias"] != $alias) {
					continue;
				}

				$result[$device_entry["channel"]] = array_filter($connection, function($i) use($alias) {
					return $i["alias"] != $alias;
				});

				break;
			}
		}

		return $result;
	}

	/**
	 * Групповая установка состояния (on/off) для списка реле.
	 * Targets: строка (alias одноканального реле) или [alias, channel] для многоканального.
	 * Автоматически синхронизирует индикаторы кнопок через sync_map.
	 * Возвращает массив результатов по каждому target.
	 */
	public function group_set_state(Array $targets, bool $state, Array $sync_map): Array {
		$results = [];
		foreach($targets as $target) {
			$relay_alias = null;
			$channel = null;
			if(is_string($target)) {
				$parts = explode(":", $target, 2);
				$relay_alias = $parts[0];
				if(isset($parts[1])) {
					$channel = intval($parts[1]);
				}
			} elseif(is_array($target) && count($target) >= 2) {
				$relay_alias = $target[0];
				$channel = intval($target[1]);
			} else {
				continue;
			}

			$relay = $this -> devices() -> by_alias($relay_alias);
			$key = $relay_alias . ($channel !== null ? ":{$channel}" : "");
			if(!$relay) {
				$results[$key] = false;
				continue;
			}

			$relay_api = $relay -> device_api();
			$result = false;
			if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) {
				if($channel === null) {
					$result = $relay_api -> set_state($state);
				} else {
					$status = $relay_api -> get_status();
					$current = $status["channels"][$channel]["state"] ?? null;
					if($current != ($state ? "on" : "off")) {
						$result = $relay_api -> set_channel_state($state, $channel);
					} else {
						$result = true;
					}
				}
				$this -> sync_relay_to_btns($sync_map, $relay_alias);
			}
			$results[$key] = $result;
		}
		return $results;
	}

	/**
	 * Групповой toggle для списка реле.
	 * Targets: строка (alias) или [alias, channel].
	 * Автоматически синхронизирует индикаторы кнопок через sync_map.
	 */
	public function group_toggle(Array $targets, Array $sync_map): Array {
		$results = [];
		foreach($targets as $target) {
			$relay_alias = null;
			$channel = null;
			if(is_string($target)) {
				$parts = explode(":", $target, 2);
				$relay_alias = $parts[0];
				if(isset($parts[1])) {
					$channel = intval($parts[1]);
				}
			} elseif(is_array($target) && count($target) >= 2) {
				$relay_alias = $target[0];
				$channel = intval($target[1]);
			} else {
				continue;
			}

			$relay = $this -> devices() -> by_alias($relay_alias);
			$key = $relay_alias . ($channel !== null ? ":{$channel}" : "");
			if(!$relay) {
				$results[$key] = false;
				continue;
			}

			$relay_api = $relay -> device_api();
			$result = false;
			if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) {
				$result = $relay_api -> toggle_channel($channel ?? 0);
				$this -> sync_relay_to_btns($sync_map, $relay_alias);
			}
			$results[$key] = $result;
		}
		return $results;
	}

	/**
	 * Получить состояние канала реле.
	 * @return bool|null — true = on, false = off, null = недоступно
	 */
	public function get_relay_state(string $relay_alias, int $channel = 0): ?bool {
		try {
			$relay = $this -> devices() -> by_alias($relay_alias);
			if(!$relay) {
				return null;
			}
			$relay_api = $relay -> device_api();
			if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
				return null;
			}
			$status = $relay_api -> get_status();
			if(empty($status)) {
				return null;
			}
			$channels = $status["channels"] ?? [];
			if(!isset($channels[$channel]["state"])) {
				return null;
			}
			return $channels[$channel]["state"] == "on";
		} catch(\Throwable $e) {
			return null;
		}
	}

	/**
	 * Получить состояние люка.
	 * @return string|null — "open", "closed" или null
	 */
	public function get_hatch_state(string $hatch_alias): ?string {
		$info = $this -> get_hatch_info($hatch_alias);
		return $info ? $info['state'] : null;
	}

	/**
	 * Получить полную информацию о люке.
	 * @return array|null — ['state', 'position_sec', 'position_pct'] или null
	 */
	public function get_hatch_info(string $hatch_alias): ?array {
		try {
			$hatch = $this -> devices() -> by_alias($hatch_alias);
			if(!$hatch) {
				return null;
			}
			$hatch_api = $hatch -> device_api();
			if(!($hatch_api instanceof \SHServ\Tools\DeviceAPI\Hatch)) {
				return null;
			}
			$status = $hatch_api -> get_status();
			if(empty($status) || ($status['status'] ?? null) != 'ok') {
				return null;
			}
			$hatch_data = $status['hatch'] ?? null;
			if(!is_array($hatch_data) || !isset($hatch_data['state'])) {
				return null;
			}
			return [
				'state'        => $hatch_data['state'],
				'position_sec' => $hatch_data['position_sec'] ?? null,
				'position_pct' => $hatch_data['position_pct'] ?? null,
			];
		} catch(\Throwable $e) {
			return null;
		}
	}

	/**
	 * Сформировать индикатор для одиночного реле.
	 * @return array — [["label" => "On|Off|?", "variant" => "success|secondary|secondary"]]
	 */
	public function make_relay_indicator(string $relay_alias, int $channel = 0): array {
		$state = $this -> get_relay_state($relay_alias, $channel);
		if($state === null) {
			return [["label" => "?", "variant" => "secondary"]];
		}
		return [
			["label" => $state ? "On" : "Off", "variant" => $state ? "success" : "secondary"]
		];
	}

	/**
	 * Сформировать индикаторы для группы реле.
	 * @param array $targets — строки вида "alias:channel" или просто "alias"
	 * @return array — массив индикаторов
	 */
	public function make_group_indicators(array $targets): array {
		$indicators = [];
		foreach($targets as $target) {
			$parts = explode(":", $target, 2);
			$alias = $parts[0];
			$channel = isset($parts[1]) ? intval($parts[1]) : 0;
			$state = $this -> get_relay_state($alias, $channel);
			if($state === null) {
				$indicators[] = ["label" => "?", "variant" => "secondary"];
			} else {
				$indicators[] = ["label" => $state ? "On" : "Off", "variant" => $state ? "success" : "secondary"];
			}
		}
		return $indicators;
	}

	/**
	 * Сформировать индикатор для люка.
	 * @return array — [["label" => "Открыт — 50%", "variant" => "success|warning|secondary|primary"]]
	 */
	public function make_hatch_indicator(string $hatch_alias): array {
		$info = $this -> get_hatch_info($hatch_alias);
		if($info === null) {
			return [["label" => "?", "variant" => "secondary"]];
		}

		$state = $info['state'];
		$pct   = $info['position_pct'];

		$labels = [
			'closed'      => 'Закрыт',
			'open'        => 'Открыт',
			'opening'     => 'Открывается',
			'closing'     => 'Закрывается',
			'calibrating' => 'Калибровка',
		];
		$variants = [
			'closed'      => 'secondary',
			'open'        => 'success',
			'opening'     => 'warning',
			'closing'     => 'warning',
			'calibrating' => 'primary',
		];

		$label = $labels[$state] ?? $state;

		// Показываем позицию для open / opening / closing
		if($pct !== null && in_array($state, ['open', 'opening', 'closing'], true)) {
			$label .= ' — ' . $pct . '%';
		}

		return [
			["label" => $label, "variant" => $variants[$state] ?? 'secondary']
		];
	}
}
