<?php

use PHPUnit\Framework\TestCase;
use SHServ\Models\Devices;
use SHServ\Tools\DeviceAPI\Base;

class DevicesModelTransactionTest extends TestCase {
	private $tb;
	private $devicesModel;

	protected function setUp(): void {
		$this -> tb = app() -> thin_builder;
		$this -> create_devices_table();
		$this -> create_device_auth_table();
		$this -> devicesModel = new Devices();
	}

	protected function tearDown(): void {
		$this -> tb -> query("DROP TABLE IF EXISTS devices");
		$this -> tb -> query("DROP TABLE IF EXISTS device_auth");
	}

	private function create_devices_table(): void {
		$this -> tb -> query("CREATE TABLE devices (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			area_id INTEGER DEFAULT 0,
			alias TEXT,
			name TEXT,
			device_type TEXT,
			device_ip TEXT,
			device_mac TEXT,
			device_hard_id TEXT,
			firmware_version TEXT,
			connection_status TEXT,
			status TEXT,
			description TEXT,
			last_contact TEXT,
			create_at TEXT,
			update_at TEXT,
			UNIQUE(device_hard_id, status)
		)");
	}

	private function create_device_auth_table(): void {
		$this -> tb -> query("CREATE TABLE device_auth (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			device_id INTEGER,
			device_token TEXT,
			status TEXT,
			create_at TEXT,
			update_at TEXT
		)");
	}

	public function test_connect_new_device_happy_path(): void {
		$mockApi = new MockDeviceAPIForSetup([
			'data' => [
				'status' => 'setup',
				'device_type' => 'relay',
				'ip_address' => '192.168.1.50',
				'mac_address' => 'AA:BB:CC:DD:EE:FF',
				'device_id' => 'hard_123',
				'firmware_version' => '1.0.0',
			]
		]);

		$result = $this -> devicesModel -> connect_new_device(
			'192.168.1.50', 'relay_kitchen', 'Kitchen Relay', '', $mockApi
		);

		$this -> assertInstanceOf(\SHServ\Entities\Device::class, $result);
		$this -> assertGreaterThan(0, $result -> id());
		$this -> assertSame('relay_kitchen', $result -> alias);
		$this -> assertSame('Kitchen Relay', $result -> name);

		$fromDb = $this -> devicesModel -> by_id($result -> id());
		$this -> assertNotNull($fromDb);
		$this -> assertSame('relay', $fromDb -> device_type);

		$auth = $result -> auth();
		$this -> assertNotNull($auth);
		$this -> assertSame('active', $auth -> status);
	}

	public function test_connect_new_device_rollback_on_auth_failure(): void {
		$this -> tb -> query("DROP TABLE device_auth");

		$mockApi = new MockDeviceAPIForSetup([
			'data' => [
				'status' => 'setup',
				'device_type' => 'relay',
				'ip_address' => '192.168.1.51',
				'mac_address' => 'AA:BB:CC:DD:EE:00',
				'device_id' => 'hard_456',
				'firmware_version' => '1.0.0',
			]
		]);

		$result = $this -> devicesModel -> connect_new_device(
			'192.168.1.51', 'relay_fail', 'Fail Relay', '', $mockApi
		);

		$this -> assertIsArray($result);
		$this -> assertFalse($result['result']);
		$this -> assertSame('db_error', $result['err_alias']);

		$fromDb = $this -> devicesModel -> by_alias('relay_fail');
		$this -> assertNull($fromDb);
	}

	public function test_connect_new_device_rejects_non_setup_mode(): void {
		$mockApi = new MockDeviceAPIForSetup([
			'data' => [
				'status' => 'normal',
				'device_type' => 'relay',
				'ip_address' => '192.168.1.52',
				'mac_address' => 'AA:BB:CC:DD:EE:11',
				'device_id' => 'hard_789',
				'firmware_version' => '1.0.0',
			]
		]);

		$result = $this -> devicesModel -> connect_new_device(
			'192.168.1.52', 'relay_normal', 'Normal Relay', '', $mockApi
		);

		$this -> assertIsArray($result);
		$this -> assertSame('device_mode_error', $result['err_alias']);
	}

	public function test_connect_new_device_returns_error_when_not_found(): void {
		$mockApi = new MockDeviceAPIForSetup([
			'data' => null,
		]);

		$result = $this -> devicesModel -> connect_new_device(
			'192.168.1.53', 'relay_missing', 'Missing Relay', '', $mockApi
		);

		$this -> assertIsArray($result);
		$this -> assertSame('device_not_found', $result['err_alias']);
	}

	public function test_connect_new_device_rejects_duplicate_hard_id(): void {
		$mockApi = new MockDeviceAPIForSetup([
			'data' => [
				'status' => 'setup',
				'device_type' => 'relay',
				'ip_address' => '192.168.1.54',
				'mac_address' => 'AA:BB:CC:DD:EE:22',
				'device_id' => 'hard_duplicate',
				'firmware_version' => '1.0.0',
			]
		]);

		$result1 = $this -> devicesModel -> connect_new_device(
			'192.168.1.54', 'relay_first', 'First Relay', '', $mockApi
		);
		$this -> assertInstanceOf(\SHServ\Entities\Device::class, $result1);

		$result2 = $this -> devicesModel -> connect_new_device(
			'192.168.1.54', 'relay_second', 'Second Relay', '', $mockApi
		);
		$this -> assertIsArray($result2);
		$this -> assertFalse($result2['result']);
		$this -> assertSame('device_already_registered', $result2['err_alias']);
	}
}

class MockDeviceAPIForSetup extends Base {
	private array $aboutResponse;

	public function __construct(array $aboutResponse) {
		parent::__construct('127.0.0.1');
		$this -> aboutResponse = $aboutResponse;
	}

	public function get_about(): array {
		return $this -> aboutResponse;
	}
}
