<?php

use PHPUnit\Framework\TestCase;

class AppAuthGuardTest extends TestCase {
	private $originalApp;
	private $testApp;

	protected function setUp(): void {
		$this -> originalApp = app();
		$this -> testApp = new TestableAppForGuard();
		\Fury\Kernel\AppContainer::set_app($this -> testApp);
		$this -> create_sessions_table();
	}

	protected function tearDown(): void {
		\Fury\Kernel\AppContainer::set_app($this -> originalApp);
		$_SERVER = [];
		$_COOKIE = [];
		app() -> thin_builder -> query("DROP TABLE IF EXISTS sessions");
	}

	private function create_sessions_table(): void {
		app() -> thin_builder -> query("CREATE TABLE sessions (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			uid INTEGER,
			status INTEGER DEFAULT 1,
			token TEXT,
			last_using_at TEXT,
			update_at TEXT,
			create_at TEXT
		)");
	}

	public function test_non_api_uri_returns_null(): void {
		$_SERVER['REQUEST_URI'] = '/index.php';
		$result = $this -> testApp -> check_api_auth();
		$this -> assertNull($result);
	}

	public function test_valid_bearer_token_passes(): void {
		$token = app() -> sessions -> create(1); // create session for user 1
		$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
		$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $token;

		$result = $this -> testApp -> check_api_auth();
		$this -> assertNull($result);
	}

	public function test_valid_cookie_token_passes(): void {
		$token = app() -> sessions -> create(1);
		$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
		$_COOKIE['auth_token'] = $token;

		$result = $this -> testApp -> check_api_auth();
		$this -> assertNull($result);
	}

	public function test_missing_token_returns_401(): void {
		$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';

		$result = $this -> testApp -> check_api_auth();
		$this -> assertNotNull($result);
		$this -> assertSame(401, $result['code']);
		$this -> assertSame('unauthorized', $result['body']['error_alias']);
	}

	public function test_invalid_token_returns_401(): void {
		$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
		$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer invalid_token_123';

		$result = $this -> testApp -> check_api_auth();
		$this -> assertNotNull($result);
		$this -> assertSame(401, $result['code']);
	}

	public function test_rate_limit_exceeded_returns_429(): void {
		$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
		$_SERVER['REMOTE_ADDR'] = '1.2.3.4';

		// Exhaust rate limit (60 requests in 1 minute window)
		for ($i = 0; $i < 60; $i++) {
			$this -> testApp -> check_api_auth();
		}

		$result = $this -> testApp -> check_api_auth();
		$this -> assertNotNull($result);
		$this -> assertSame(429, $result['code']);
		$this -> assertSame('rate_limit_exceeded', $result['body']['error_alias']);
	}

	public function test_rate_limit_resets_after_window(): void {
		$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
		$_SERVER['REMOTE_ADDR'] = '1.2.3.5';

		// Exhaust limit
		for ($i = 0; $i < 60; $i++) {
			$this -> testApp -> check_api_auth();
		}
		$result = $this -> testApp -> check_api_auth();
		$this -> assertSame(429, $result['code']);

		// Simulate time passing: manipulate stored timestamps via reflection
		$ref = new \ReflectionClass(\SHServ\Tools\RateLimiter::class);
		$prop = $ref -> getProperty('requests');
		$prop -> setAccessible(true);
		$prop -> setValue(null, []);

		$token = app() -> sessions -> create(1);
		$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $token;

		$result = $this -> testApp -> check_api_auth();
		$this -> assertNull($result);
	}
}

class TestableAppForGuard extends \SHServ\App {
	public function app_init(): void {
		$this -> utils = new \SHServ\Utils();
		$this -> sessions = new \SHServ\Sessions();
	}
}
