<?php

namespace Licenzo\Services;

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

use Licenzo\Model\IdempotencyKey;

class IdempotencyService
{
    /**
     * Attempt to reserve an idempotency key for processing.
     *
     * Returns false when the key already exists.
     */
    public function acquire(
        string $provider,
        string $eventScope,
        string $eventKey,
        ?int $subscriptionId = null,
        ?int $customerId = null,
        ?string $payloadChecksum = null,
        ?string $expiresAt = null
    ): bool {
        $provider = trim($provider);
        $eventScope = trim($eventScope);
        $eventKey = trim($eventKey);

        if ($provider === '' || $eventScope === '' || $eventKey === '') {
            throw new \InvalidArgumentException('Provider, event scope, and event key are required.');
        }

        $keyHash = IdempotencyKey::hash($provider, $eventScope, $eventKey);

        $existing = IdempotencyKey::where('key_hash', $keyHash)->first();
        if ($existing) {
            if ($existing->status === IdempotencyKey::STATUS_FAILED) {
                return $existing->update([
                    'status' => IdempotencyKey::STATUS_PROCESSING,
                    'processed_at' => null,
                    'payload_checksum' => $payloadChecksum,
                    'expires_at' => $expiresAt,
                ]);
            }

            return false;
        }

        try {
            IdempotencyKey::create([
                'provider' => $provider,
                'event_scope' => $eventScope,
                'event_key' => $eventKey,
                'key_hash' => $keyHash,
                'subscription_id' => $subscriptionId,
                'customer_id' => $customerId,
                'status' => IdempotencyKey::STATUS_PROCESSING,
                'payload_checksum' => $payloadChecksum,
                'expires_at' => $expiresAt,
            ]);
            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Mark an idempotency key as processed.
     */
    public function markProcessed(string $provider, string $eventScope, string $eventKey): bool
    {
        $record = $this->findByKey($provider, $eventScope, $eventKey);
        if (!$record) {
            return false;
        }

        return $record->update([
            'status' => IdempotencyKey::STATUS_PROCESSED,
            'processed_at' => gmdate('Y-m-d H:i:s'),
        ]);
    }

    /**
     * Mark an idempotency key as failed.
     */
    public function markFailed(string $provider, string $eventScope, string $eventKey): bool
    {
        $record = $this->findByKey($provider, $eventScope, $eventKey);
        if (!$record) {
            return false;
        }

        return $record->update([
            'status' => IdempotencyKey::STATUS_FAILED,
        ]);
    }

    /**
     * Check if a key is already processed.
     */
    public function isProcessed(string $provider, string $eventScope, string $eventKey): bool
    {
        $record = $this->findByKey($provider, $eventScope, $eventKey);
        if (!$record) {
            return false;
        }

        return $record->status === IdempotencyKey::STATUS_PROCESSED;
    }

    /**
     * Find an idempotency record by key parts.
     */
    public function findByKey(string $provider, string $eventScope, string $eventKey): ?IdempotencyKey
    {
        $keyHash = IdempotencyKey::hash($provider, $eventScope, $eventKey);
        return IdempotencyKey::where('key_hash', $keyHash)->first();
    }
}
