<?php

namespace Licenzo\Services\Logging;

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

use Licenzo\Framework\Database\Model;
use Licenzo\Framework\Http\Request;
use Licenzo\Framework\Support\Collection;
use Licenzo\Model\WebhookLog as WebhookLogModel;
use Licenzo\Services\Logging\Concerns\CapturesContext;
use Licenzo\Services\Logging\Concerns\HandlesErrors;

/**
 * WebhookLog Logger
 * 
 * Automatically logs incoming webhooks with complete audit trail.
 * Minimal user input required - auto-populates all fields from context.
 * 
 * Usage:
 *   WebhookLog::log('stripe', 'customer.subscription.updated', $payload);
 *   WebhookLog::log('stripe', 'invoice.payment_succeeded', $payload, $request);
 *   WebhookLog::log('stripe', 'charge.succeeded', $payload, null, 'evt_123abc');
 */
class WebhookLog
{
    use CapturesContext, HandlesErrors;

    /**
     * Log an incoming webhook with minimal input
     *
     * @param string $provider Provider name (e.g., 'stripe', 'paypal', 'woocommerce')
     * @param string $eventType Provider's event type (e.g., 'customer.subscription.updated')
     * @param array $payload Full webhook payload
     * @param Request|null $request Optional request object
     * @param string|null $eventId Optional provider's event ID
     * @return WebhookLogModel|null
     */
    public static function log(
        string $provider,
        string $eventType,
        array $payload,
        ?Request $request = null,
        ?string $eventId = null
    ): ?WebhookLogModel {
        return static::safeExecute(function () use ($provider, $eventType, $payload, $request, $eventId) {
            // Build complete webhook data
            $data = static::buildWebhookData($provider, $eventType, $payload, $request, $eventId);

            // Create and return the webhook log
            return WebhookLogModel::create($data);
        }, 'WebhookLog::log');
    }

    /**
     * Mark webhook as processed
     *
     * @param WebhookLogModel $log
     * @param Model|null $relatedEvent Optional related event that was created
     * @return bool
     */
    public static function markProcessed(WebhookLogModel $log, ?Model $relatedEvent = null): bool
    {
        return static::safeExecute(function () use ($log, $relatedEvent) {
            $updateData = [
                'status' => WebhookLogModel::STATUS_PROCESSED,
                'processed_at' => gmdate('Y-m-d H:i:s'),
            ];

            // Optionally store related event info in payload
            if ($relatedEvent) {
                $payload = $log->payload;
                $payload['_processed_event'] = [
                    'type' => class_basename($relatedEvent),
                    'id' => $relatedEvent->id,
                ];
                $updateData['payload'] = $payload;
            }

            return $log->update($updateData);
        }, 'WebhookLog::markProcessed') ?? false;
    }

    /**
     * Mark webhook as failed
     *
     * @param WebhookLogModel $log
     * @param string $errorMessage
     * @param bool $shouldRetry Whether to increment retry count
     * @return bool
     */
    public static function markFailed(WebhookLogModel $log, string $errorMessage, bool $shouldRetry = true): bool
    {
        return static::safeExecute(function () use ($log, $errorMessage, $shouldRetry) {
            $updateData = [
                'status' => WebhookLogModel::STATUS_FAILED,
                'error_message' => $errorMessage,
            ];

            if ($shouldRetry) {
                $updateData['retry_count'] = $log->retry_count + 1;
            }

            return $log->update($updateData);
        }, 'WebhookLog::markFailed') ?? false;
    }

    /**
     * Retry a failed webhook
     *
     * @param WebhookLogModel $log
     * @param callable $callback Callback to process the webhook
     * @return bool
     */
    public static function retry(WebhookLogModel $log, callable $callback): bool
    {
        return static::safeExecute(function () use ($log, $callback) {
            // Check if can retry
            if (!$log->canRetry()) {
                return false;
            }

            try {
                // Execute the callback
                $result = $callback($log);

                // Mark as processed if successful
                if ($result) {
                    return static::markProcessed($log);
                }

                // Mark as failed if unsuccessful
                return static::markFailed($log, 'Retry callback returned false', true);
            } catch (\Throwable $e) {
                // Mark as failed with error
                static::markFailed($log, $e->getMessage(), true);
                static::handleError($e, 'WebhookLog::retry');
                return false;
            }
        }, 'WebhookLog::retry') ?? false;
    }

    /**
     * Get pending webhooks
     *
     * @param string|null $provider Optional provider filter
     * @return Collection
     */
    public static function getPending(?string $provider = null): Collection
    {
        $query = WebhookLogModel::where('status', WebhookLogModel::STATUS_PENDING);

        if ($provider) {
            $query->where('provider', $provider);
        }

        return $query->orderBy('created_at', 'asc')->get();
    }

    /**
     * Get failed webhooks that can be retried
     *
     * @param string|null $provider Optional provider filter
     * @return Collection
     */
    public static function getRetryable(?string $provider = null): Collection
    {
        $query = WebhookLogModel::where('status', WebhookLogModel::STATUS_FAILED)
            ->where('retry_count', '<', WebhookLogModel::MAX_RETRY_COUNT);

        if ($provider) {
            $query->where('provider', $provider);
        }

        return $query->orderBy('created_at', 'asc')->get();
    }

    /**
     * Get webhooks by provider
     *
     * @param string $provider
     * @param string|null $status Optional status filter
     * @param int $limit
     * @return Collection
     */
    public static function getByProvider(string $provider, ?string $status = null, int $limit = 100): Collection
    {
        $query = WebhookLogModel::where('provider', $provider);

        if ($status) {
            $query->where('status', $status);
        }

        return $query->orderBy('created_at', 'desc')->limit($limit)->get();
    }

    /**
     * Get webhooks by event type
     *
     * @param string $eventType
     * @param string|null $provider Optional provider filter
     * @param int $limit
     * @return Collection
     */
    public static function getByEventType(string $eventType, ?string $provider = null, int $limit = 100): Collection
    {
        $query = WebhookLogModel::where('event_type', $eventType);

        if ($provider) {
            $query->where('provider', $provider);
        }

        return $query->orderBy('created_at', 'desc')->limit($limit)->get();
    }

    /**
     * Build complete webhook data with auto-population
     *
     * @param string $provider
     * @param string $eventType
     * @param array $payload
     * @param Request|null $request
     * @param string|null $eventId
     * @return array
     */
    protected static function buildWebhookData(
        string $provider,
        string $eventType,
        array $payload,
        ?Request $request,
        ?string $eventId
    ): array {
        return [
            // Provider details
            'provider' => $provider,
            'event_type' => $eventType,
            'event_id' => $eventId,

            // Webhook data
            'payload' => $payload,
            'headers' => static::extractHeaders($request),

            // Processing status
            'status' => WebhookLogModel::STATUS_PENDING,
            'error_message' => null,
            'retry_count' => 0,

            // Timestamps
            'processed_at' => null,
        ];
    }

    /**
     * Reset webhook to pending for reprocessing
     *
     * @param WebhookLogModel $log
     * @return bool
     */
    public static function resetToPending(WebhookLogModel $log): bool
    {
        return static::safeExecute(function () use ($log) {
            return $log->update([
                'status' => WebhookLogModel::STATUS_PENDING,
                'error_message' => null,
                'processed_at' => null,
            ]);
        }, 'WebhookLog::resetToPending') ?? false;
    }

    /**
     * Get webhook statistics
     *
     * @param string|null $provider Optional provider filter
     * @return array
     */
    public static function getStatistics(?string $provider = null): array
    {
        $query = WebhookLogModel::query();

        if ($provider) {
            $query->where('provider', $provider);
        }

        return [
            'total' => (clone $query)->count(),
            'pending' => (clone $query)->where('status', WebhookLogModel::STATUS_PENDING)->count(),
            'processed' => (clone $query)->where('status', WebhookLogModel::STATUS_PROCESSED)->count(),
            'failed' => (clone $query)->where('status', WebhookLogModel::STATUS_FAILED)->count(),
            'retryable' => (clone $query)
                ->where('status', WebhookLogModel::STATUS_FAILED)
                ->where('retry_count', '<', WebhookLogModel::MAX_RETRY_COUNT)
                ->count(),
        ];
    }
}

