<?php

namespace App\Framework\Smartlinks\Services;

use App\Framework\DateTime\Clock;
use App\Framework\Smartlinks\SmartlinkData;
use App\Framework\Smartlinks\SmartLinkToken;
use App\Framework\Smartlinks\TokenAction;
use App\Framework\Smartlinks\TokenConfig;
use App\Framework\Ulid\UlidGenerator;

final class InMemorySmartLinkService implements SmartlinkService
{
    private array $tokens = [];

    public function __construct(
        private readonly UlidGenerator $ulidGenerator,
        private readonly Clock $clock
    ) {}

    public function generate(
        TokenAction $action,
        array                   $payload,
        ?TokenConfig            $config = null,
        ?string                 $createdByIp = null,
        ?string                 $userAgent = null
    ): SmartlinkToken {
        $config ??= new TokenConfig();
        $id = $this->ulidGenerator->generate($this->clock);;
        $now = new \DateTimeImmutable();

        $smartlinkData = new SmartlinkData(
            id: $id,
            action: $action,
            payload: $payload,
            expiresAt: $config->getExpiryDateTime(),
            createdAt: $now,
            oneTimeUse: $config->oneTimeUse,
            createdByIp: $createdByIp,
            userAgent: $userAgent
        );

        $token = new SmartlinkToken($id);
        $this->tokens[$id] = $smartlinkData;

        return $token;
    }

    public function validate(SmartlinkToken $token): ?SmartlinkData
    {
        if (!isset($this->tokens[$token->value])) {
            return null;
        }

        $data = $this->tokens[$token->value];

        if (!$data->isValid()) {
            return null;
        }

        return $data;
    }

    public function markAsUsed(SmartlinkToken $token): void
    {
        if (isset($this->tokens[$token->value])) {
            $data = $this->tokens[$token->value];
            $this->tokens[$token->value] = $data->withUsed(new \DateTimeImmutable());
        }
    }

    public function revoke(SmartlinkToken $token): void
    {
        unset($this->tokens[$token->value]);
    }

    public function exists(SmartlinkToken $token): bool
    {
        return isset($this->tokens[$token->value]) &&
            $this->tokens[$token->value]->isValid();
    }

    public function getActiveTokens(int $limit = 100): array
    {
        return array_slice(
            array_filter($this->tokens, fn($data) => $data->isValid()),
            0,
            $limit
        );
    }

    public function cleanupExpired(): int
    {
        $count = 0;
        foreach ($this->tokens as $id => $data) {
            if ($data->isExpired()) {
                unset($this->tokens[$id]);
                $count++;
            }
        }
        return $count;
    }
}
