<?php

declare(strict_types=1);

namespace App\Framework\OAuth\ValueObjects;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Timestamp;

/**
 * Access Token Value Object
 *
 * Represents an OAuth access token with validation and masking
 */
final readonly class AccessToken
{
    public function __construct(
        private string $token,
        private Timestamp $expiresAt,
    ) {
        if (empty($token)) {
            throw new \InvalidArgumentException('Access token cannot be empty');
        }

        if (strlen($token) < 10) {
            throw new \InvalidArgumentException('Access token appears invalid (too short)');
        }
    }

    public static function create(string $token, Timestamp $expiresAt): self
    {
        return new self($token, $expiresAt);
    }

    /**
     * Create from provider response with expires_in seconds
     */
    public static function fromProviderResponse(string $token, int $expiresIn): self
    {
        return new self(
            $token,
            Timestamp::now()->add(Duration::fromSeconds($expiresIn))
        );
    }

    /**
     * Get the raw token value
     */
    public function toString(): string
    {
        return $this->token;
    }

    /**
     * Get expiration timestamp
     */
    public function getExpiresAt(): Timestamp
    {
        return $this->expiresAt;
    }

    /**
     * Check if token is expired
     */
    public function isExpired(): bool
    {
        // Add 60 second buffer for network latency
        return $this->expiresAt->isBefore(Timestamp::now()->add(Duration::fromSeconds(60)));
    }

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

    /**
     * Get seconds until expiration
     */
    public function getSecondsUntilExpiration(): int
    {
        $now = Timestamp::now();
        if ($this->expiresAt->isBefore($now)) {
            return 0;
        }

        return $this->expiresAt->toTimestamp() - $now->toTimestamp();
    }

    /**
     * Get masked token for logging (shows first/last 4 chars)
     */
    public function getMasked(): string
    {
        if (strlen($this->token) <= 12) {
            return str_repeat('*', strlen($this->token));
        }

        $first = substr($this->token, 0, 4);
        $last = substr($this->token, -4);
        $masked = str_repeat('*', strlen($this->token) - 8);

        return $first . $masked . $last;
    }

    /**
     * Create new token with updated expiration
     */
    public function withExpiresAt(Timestamp $expiresAt): self
    {
        return new self($this->token, $expiresAt);
    }

    public function __toString(): string
    {
        return $this->getMasked();
    }
}
