<?php

declare(strict_types=1);

namespace SHServ\Integrations\GAuth\Store;

use GNexus\GAuth\Contract\TokenStoreInterface;
use GNexus\GAuth\DTO\TokenSet;
use Fury\Modules\ThinBuilder\ThinBuilder;

final class DbTokenStore implements TokenStoreInterface
{
    private ThinBuilder $tb;

    public function __construct(ThinBuilder $tb)
    {
        $this->tb = $tb;
    }

    public function put(string $key, TokenSet $tokenSet, ?int $userId = null): void
    {
        $expiresAt = $tokenSet->expiresAt;
        $data = [
            'session_token' => $key,
            'access_token' => $tokenSet->accessToken,
            'refresh_token' => $tokenSet->refreshToken,
            'expires_at' => $expiresAt ? $expiresAt->format('Y-m-d H:i:s') : null,
            'ip_address' => $this->getClientIp(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
            'updated_at' => date('Y-m-d H:i:s'),
        ];

        if ($userId !== null) {
            $data['user_id'] = $userId;
        }

        $existing = $this->tb->select('shserv_sessions', ['id'], [['session_token', '=', $key]]);
        if ($existing) {
            $this->tb->update('shserv_sessions', $data, [['session_token', '=', $key]]);
        } else {
            $this->tb->insert('shserv_sessions', $data);
        }
    }

    public function get(string $key): ?TokenSet
    {
        $result = $this->tb->select('shserv_sessions', ['access_token', 'refresh_token', 'expires_at'], [['session_token', '=', $key]]);

        if (!$result) {
            return null;
        }

        $row = $result[0];
        $expiresAt = $row['expires_at'] ? new \DateTimeImmutable($row['expires_at']) : null;

        return new TokenSet(
            accessToken: (string) $row['access_token'],
            refreshToken: isset($row['refresh_token']) ? (string) $row['refresh_token'] : null,
            tokenType: 'Bearer',
            expiresIn: $expiresAt ? (int) $expiresAt->format('U') - time() : 0,
            expiresAt: $expiresAt,
        );
    }

    public function forget(string $key): void
    {
        $this->tb->delete('shserv_sessions', [['session_token', '=', $key]]);
    }

    public function updateUserId(string $key, int $userId): void
    {
        $this->tb->update('shserv_sessions', [
            'user_id' => $userId,
            'updated_at' => date('Y-m-d H:i:s'),
        ], [['session_token', '=', $key]]);
    }

    private function getClientIp(): ?string
    {
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = trim($ips[0]);
            if (filter_var($ip, FILTER_VALIDATE_IP)) {
                return $ip;
            }
        }
        return $_SERVER['REMOTE_ADDR'] ?? null;
    }
}
