<?php

namespace Licenzo\Services\Updates;

use Licenzo\Model\Meta;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

class DownloadTokenService
{
    private const ENTITY_TYPE = 'download_token';
    private const TTL_SECONDS = 3600;
    private const GRACE_SECONDS = 300;
    private const CLEANUP_PROBABILITY = 50;

    public function generate(int $licenseId, int $variationId, string $releaseUuid): string
    {
        $token = bin2hex(random_bytes(32));

        // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
        Meta::create([
            'entity_type' => static::ENTITY_TYPE,
            'entity_id'   => $licenseId,
            'meta_key'    => $token,
            'meta_value'  => [
                'variation_id' => $variationId,
                'release_uuid' => $releaseUuid,
                'expires_at'   => gmdate('Y-m-d H:i:s', time() + static::TTL_SECONDS),
                'consumed_at'  => null,
            ],
        ]);
        // phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value

        $this->maybeCleanup();

        return $token;
    }

    /**
     * @return array{meta: Meta, payload: array<string,mixed>}|null
     */
    public function validate(string $token, int $licenseId, int $variationId, string $releaseUuid): ?array
    {
        $token = sanitize_text_field($token);
        if ($token === '') {
            return null;
        }

        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Eloquent lookup by token value.
        $row = Meta::where('entity_type', static::ENTITY_TYPE)
            ->where('meta_key', $token)
            ->first();

        if (!$row) {
            return null;
        }

        $payload = is_array($row->meta_value) ? $row->meta_value : [];

        $expiresAt = strtotime((string) ($payload['expires_at'] ?? ''));
        if (!$expiresAt || $expiresAt < time()) {
            return null;
        }

        if ((int) $row->entity_id !== $licenseId) {
            return null;
        }

        if ((int) ($payload['variation_id'] ?? 0) !== $variationId) {
            return null;
        }

        if ((string) ($payload['release_uuid'] ?? '') !== $releaseUuid) {
            return null;
        }

        $consumedAt = $payload['consumed_at'] ?? null;
        if ($consumedAt !== null) {
            $consumedTime = strtotime((string) $consumedAt);
            if ($consumedTime && (time() - $consumedTime) > static::GRACE_SECONDS) {
                return null;
            }
        }

        return ['meta' => $row, 'payload' => $payload];
    }

    public function consume(Meta $row): void
    {
        $payload = is_array($row->meta_value) ? $row->meta_value : [];
        $payload['consumed_at'] = gmdate('Y-m-d H:i:s');

        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Eloquent model update.
        $row->update(['meta_value' => $payload]);
    }

    public function revoke(Meta $row): void
    {
        $row->delete();
    }

    private function maybeCleanup(): void
    {
        if (wp_rand(1, static::CLEANUP_PROBABILITY) !== 1) {
            return;
        }

        $cutoff = gmdate('Y-m-d H:i:s', time() - 86400);

        $expired = Meta::where('entity_type', static::ENTITY_TYPE)
            ->where('created_at', '<', $cutoff)
            ->get();

        foreach ($expired as $row) {
            $row->delete();
        }
    }
}
