<?php declare(strict_types=1);
namespace App\Repository;

error_reporting(error_level: E_ALL);

use App\Controller\DatabaseConnection;
use App\Entity\Apikey;
use Exception;
use PDO;
use PDOException;

/**
 *
 */
class ApikeyRepository
{
	public function __construct(private readonly DatabaseConnection $databaseConnection)
	{}
	
	
	/**
	 * @return array|false
	 */
	public function findAll(): bool|array
	{
		$sql = "
            SELECT id, name, api_token_prefix, api_token
            FROM " . DatabaseConnection::TABLE_APIKEYS;
		
		try {
			$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
			$statement->execute();
			
			$apikeys = [];
			
			while ($result = $statement->fetch()) {
				$apikey = new Apikey(name: $result['name'], apiTokenPrefix: $result['api_token_prefix'], apiToken: $result['api_token'], id: $result['id']);
				$apikeys[] = $apikey;
			}
			return $apikeys;
		} catch (PDOException $e) {
			exit($e->getMessage());
		}
	}
	
	
	/**
	 * @param Int $id
	 *
	 * @return \App\Entity\Apikey|bool
	 */
	public function findByID(Int $id): Apikey|bool
	{
		$sql = "
			SELECT id, name, api_token_prefix, api_token
			FROM " . DatabaseConnection::TABLE_APIKEYS . "
            WHERE id = :id;
        ";
		
		try {
			$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
			$statement->bindParam(param: ':id', var: $id);
			$statement->execute();
			if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
				return new Apikey(name: $result['name'], apiTokenPrefix: $result['api_token_prefix'], apiToken: $result['api_token'], id: $result['id']);
			} else {
				return false;
			}
		} catch (PDOException $e) {
			exit($e->getMessage());
		}
	}
	
	
	/**
	 * @param String $prefix
	 *
	 * @return \App\Entity\Apikey|bool
	 */
	public function findByPrefix(String $prefix): Apikey|bool
	{
		$sql = "
			SELECT id, name, api_token_prefix, api_token
			FROM " . DatabaseConnection::TABLE_APIKEYS . "
            WHERE api_token_prefix = :prefix";
		
		try {
			$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
			$statement->bindParam(param: ':prefix', var: $prefix);
			$statement->execute();
			if ($result =  $statement->fetch(mode: PDO::FETCH_ASSOC)) {
				return new Apikey(name: $result['name'], apiTokenPrefix: $result['api_token_prefix'], apiToken: $result['api_token'], id: $result['id']);
			} else {
				return false;
			}
		} catch (PDOException $e) {
			exit($e->getMessage());
		}
	}
	
	
	/**
	 * @return array|void
	 */
	public function create(String $name = '')
	{
		$tokenPrefix = uniqid();
		$result['tokenPrefix'] = $tokenPrefix;
		try {
			$key = bin2hex(string: random_bytes(length: 24));
			$result['key'] = $key;
		} catch (Exception $e) {
			echo $e->getMessage() . PHP_EOL;
			exit(1);
		}
		$token = password_hash(password: $tokenPrefix . '.' . $key, algo: PASSWORD_ARGON2ID);
		
		$sql = "
            INSERT INTO " . DatabaseConnection::TABLE_APIKEYS . " (name, api_token_prefix, api_token)
            VALUES (:name, :token_prefix, :token)";
		
		try {
			$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
			$statement->bindParam(param: ':token_prefix', var: $tokenPrefix);
			$statement->bindParam(param: ':token', var: $token);
			$statement->bindParam(param: ':name', var: $name);
			
			$statement->execute();
			$result['row'] = $this->databaseConnection->getConnection()->lastInsertId();
			return $result;
		} catch (PDOException $e) {
			exit($e->getMessage());
		}
	}
	
	/**
	 * @param Int    $id
	 * @param String $name
	 *
	 * @return false|int
	 */
	public function update(int $id, string $name): bool|int
	{
		$current = $this->findByID(id: $id);
		
		if (empty($name)) {
			$name = $current['name'];
		}
		
		$sql = "
			UPDATE " . DatabaseConnection::TABLE_APIKEYS . " SET
				name = :name
            WHERE id = :id";
		
		try {
			$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
			$statement->bindParam(param: 'id', var: $id);
			$statement->bindParam(param: 'name', var: $name);
			$statement->execute();
			
			return $statement->rowCount();
		} catch (PDOException $e) {
			echo $e->getMessage();
			return false;
		}
	}
	
	
	/**
	 * @param $id
	 *
	 * @return int
	 */
	public function delete($id): int
	{
		$sql = "
            DELETE FROM " . DatabaseConnection::TABLE_APIKEYS . "
            WHERE id = :id";
		
		try {
			$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
			$statement->bindParam(param: 'id', var: $id);
			$statement->execute();
			return $statement->rowCount();
		} catch (PDOException $e) {
			exit($e->getMessage());
		}
	}
	
}
