<?php

declare(strict_types=1);

namespace App\Framework\OAuth\Storage;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\OAuth\ValueObjects\OAuthToken;

/**
 * Stored OAuth Token Entity
 *
 * Represents a persisted OAuth token with user and provider association
 */
final readonly class StoredOAuthToken
{
    public function __construct(
        public ?int $id,
        public string $userId,
        public string $provider,
        public OAuthToken $token,
        public Timestamp $createdAt,
        public Timestamp $updatedAt,
    ) {
    }

    /**
     * Create new stored token
     */
    public static function create(
        string $userId,
        string $provider,
        OAuthToken $token
    ): self {
        $now = Timestamp::now();

        return new self(
            id: null,
            userId: $userId,
            provider: $provider,
            token: $token,
            createdAt: $now,
            updatedAt: $now,
        );
    }

    /**
     * Update with refreshed token
     */
    public function withRefreshedToken(OAuthToken $token): self
    {
        return new self(
            id: $this->id,
            userId: $this->userId,
            provider: $this->provider,
            token: $token,
            createdAt: $this->createdAt,
            updatedAt: Timestamp::now(),
        );
    }

    /**
     * Check if token is expired
     */
    public function isExpired(): bool
    {
        return $this->token->isExpired();
    }

    /**
     * Check if token can be refreshed
     */
    public function canRefresh(): bool
    {
        return $this->token->canRefresh();
    }

    /**
     * Convert to array for storage
     *
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        $array = [
            'user_id' => $this->userId,
            'provider' => $this->provider,
            'access_token' => $this->token->accessToken->toString(),
            'refresh_token' => $this->token->refreshToken?->toString(),
            'token_type' => $this->token->tokenType->value,
            'scope' => $this->token->scope?->toString(),
            'expires_at' => $this->token->getExpiresAt()->toTimestamp(),
            'created_at' => $this->createdAt->toTimestamp(),
            'updated_at' => $this->updatedAt->toTimestamp(),
        ];

        if ($this->id !== null) {
            $array['id'] = $this->id;
        }

        return $array;
    }

    /**
     * Create from database row
     *
     * @param array<string, mixed> $row
     */
    public static function fromArray(array $row): self
    {
        // Convert timestamp fields if they're in string format
        $expiresAt = is_numeric($row['expires_at'])
            ? (int) $row['expires_at']
            : strtotime($row['expires_at']);

        $createdAt = is_numeric($row['created_at'])
            ? (int) $row['created_at']
            : strtotime($row['created_at']);

        $updatedAt = is_numeric($row['updated_at'])
            ? (int) $row['updated_at']
            : strtotime($row['updated_at']);

        return new self(
            id: isset($row['id']) ? (int) $row['id'] : null,
            userId: (string) $row['user_id'],
            provider: (string) $row['provider'],
            token: OAuthToken::fromArray([
                'access_token' => $row['access_token'],
                'refresh_token' => $row['refresh_token'] ?? null,
                'token_type' => $row['token_type'] ?? 'Bearer',
                'scope' => $row['scope'] ?? null,
                'expires_at' => $expiresAt,
            ]),
            createdAt: Timestamp::fromTimestamp($createdAt),
            updatedAt: Timestamp::fromTimestamp($updatedAt),
        );
    }
}
