<?php

namespace App\Infrastructure\Repositories;

use App\Domain\Entities\RefreshToken;
use App\Domain\Repositories\TokenRepositoryInterface;
use App\Drivers\CacheDriver;
use App\Drivers\DatabaseDriver;
use PDO;

class MySqlTokenRepository implements TokenRepositoryInterface
{
    private PDO $db;
    private CacheDriver $cache;

    public function __construct(DatabaseDriver $driver)
    {
        $this->db = $driver->getConnection();
        $this->cache = new CacheDriver(['path' => dirname(__DIR__, 3) . '/storage/cache']);
    }

    public function storeRefreshToken(RefreshToken $token): void
    {
        $this->ensureRefreshTable();
        $stmt = $this->db->prepare(
            'INSERT INTO auth_refresh_tokens (id, user_id, token_hash, expires_at, revoked, created_at)
             VALUES (:id, :user_id, :token_hash, FROM_UNIXTIME(:expires_at), :revoked, NOW())'
        );
        $stmt->execute([
            'id' => $token->id,
            'user_id' => $token->userId,
            'token_hash' => $token->tokenHash,
            'expires_at' => $token->expiresAt,
            'revoked' => $token->revoked ? 1 : 0,
        ]);
    }

    public function findRefreshToken(string $tokenHash): ?RefreshToken
    {
        $this->ensureRefreshTable();
        $stmt = $this->db->prepare('SELECT * FROM auth_refresh_tokens WHERE token_hash = :token_hash LIMIT 1');
        $stmt->execute(['token_hash' => $tokenHash]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            return null;
        }

        return new RefreshToken(
            $row['id'],
            (int)$row['user_id'],
            $row['token_hash'],
            strtotime($row['expires_at']),
            (bool)$row['revoked']
        );
    }

    public function revokeRefreshToken(string $tokenHash): void
    {
        $this->ensureRefreshTable();
        $stmt = $this->db->prepare('UPDATE auth_refresh_tokens SET revoked = 1, revoked_at = NOW() WHERE token_hash = :token_hash');
        $stmt->execute(['token_hash' => $tokenHash]);
    }

    public function blacklistJti(string $jti, int $expiresAt): void
    {
        $ttl = max(60, $expiresAt - time() + 60);
        $this->cache->put('jwt:blacklist:' . $jti, '1', $ttl);
    }

    public function isJtiBlacklisted(string $jti): bool
    {
        return $this->cache->has('jwt:blacklist:' . $jti);
    }

    private function ensureRefreshTable(): void
    {
        $this->db->exec(
            'CREATE TABLE IF NOT EXISTS auth_refresh_tokens (
                id VARCHAR(64) NOT NULL PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                token_hash VARCHAR(128) NOT NULL UNIQUE,
                expires_at DATETIME NOT NULL,
                revoked TINYINT(1) NOT NULL DEFAULT 0,
                revoked_at DATETIME NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                INDEX idx_refresh_user (user_id),
                INDEX idx_refresh_hash (token_hash)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
        );
    }
}
