<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class QuizWebhook extends Model
{
    use HasFactory, SoftDeletes;

    const EVENTS = [
        'quiz.created' => 'Quiz Created',
        'quiz.updated' => 'Quiz Updated',
        'quiz.published' => 'Quiz Published',
        'quiz.unpublished' => 'Quiz Unpublished',
        'quiz.deleted' => 'Quiz Deleted',
        'attempt.started' => 'Attempt Started',
        'attempt.resumed' => 'Attempt Resumed',
        'attempt.completed' => 'Attempt Completed',
        'attempt.abandoned' => 'Attempt Abandoned',
        'attempt.expired' => 'Attempt Expired',
        'question.answered' => 'Question Answered',
        'question.skipped' => 'Question Skipped',
        'feedback.submitted' => 'Feedback Submitted',
    ];

    protected $fillable = [
        'name',
        'description',
        'url',
        'events',
        'headers',
        'secret',
        'status',
        'retry_count',
        'max_retries',
        'last_triggered_at',
        'last_success_at',
        'last_failure_at',
        'last_error',
        'metadata',
        'created_by',
    ];

    protected $casts = [
        'events' => 'array',
        'headers' => 'array',
        'metadata' => 'array',
        'last_triggered_at' => 'datetime',
        'last_success_at' => 'datetime',
        'last_failure_at' => 'datetime',
    ];

    // Relationships
    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    // Scopes
    public function scopeActive(Builder $query): Builder
    {
        return $query->where('status', 'active');
    }

    public function scopeForEvent(Builder $query, string $event): Builder
    {
        return $query->whereJsonContains('events', $event);
    }

    public function scopeCanRetry(Builder $query): Builder
    {
        return $query->where('status', 'failed')
                    ->whereColumn('retry_count', '<', 'max_retries');
    }

    // Methods
    public function trigger(string $event, array $payload): bool
    {
        if (!$this->isActive() || !$this->listensToEvent($event)) {
            return false;
        }

        try {
            $this->update(['last_triggered_at' => now()]);

            $response = $this->sendWebhook($event, $payload);

            if ($response->successful()) {
                $this->markAsSuccess();
                return true;
            } else {
                $this->markAsFailure($response->body());
                return false;
            }
        } catch (\Exception $e) {
            $this->markAsFailure($e->getMessage());
            return false;
        }
    }

    public function isActive(): bool
    {
        return $this->status === 'active';
    }

    public function listensToEvent(string $event): bool
    {
        return in_array($event, $this->events ?? []);
    }

    public function canRetry(): bool
    {
        return $this->status === 'failed' && $this->retry_count < $this->max_retries;
    }

    public function markAsSuccess(): void
    {
        $this->update([
            'status' => 'active',
            'retry_count' => 0,
            'last_success_at' => now(),
            'last_error' => null,
        ]);
    }

    public function markAsFailure(string $error): void
    {
        $this->increment('retry_count');
        
        $status = $this->retry_count >= $this->max_retries ? 'failed' : 'active';
        
        $this->update([
            'status' => $status,
            'last_failure_at' => now(),
            'last_error' => $error,
        ]);
    }

    public function reset(): void
    {
        $this->update([
            'status' => 'active',
            'retry_count' => 0,
            'last_error' => null,
        ]);
    }

    private function sendWebhook(string $event, array $payload): \Illuminate\Http\Client\Response
    {
        $headers = array_merge([
            'Content-Type' => 'application/json',
            'User-Agent' => 'Quiz-Platform-Webhook/1.0',
            'X-Webhook-Event' => $event,
            'X-Webhook-Timestamp' => now()->timestamp,
        ], $this->headers ?? []);

        // Add signature if secret is provided
        if ($this->secret) {
            $signature = $this->generateSignature($payload);
            $headers['X-Webhook-Signature'] = $signature;
        }

        $webhookPayload = [
            'event' => $event,
            'timestamp' => now()->toISOString(),
            'data' => $payload,
        ];

        return Http::withHeaders($headers)
                  ->timeout(30)
                  ->post($this->url, $webhookPayload);
    }

    private function generateSignature(array $payload): string
    {
        $payloadString = json_encode($payload);
        return 'sha256=' . hash_hmac('sha256', $payloadString, $this->secret);
    }

    public static function triggerForEvent(string $event, array $payload): void
    {
        $webhooks = static::active()
                          ->forEvent($event)
                          ->get();

        foreach ($webhooks as $webhook) {
            try {
                $webhook->trigger($event, $payload);
            } catch (\Exception $e) {
                Log::error("Webhook trigger failed for {$webhook->id}: " . $e->getMessage());
            }
        }
    }

    public static function retryFailedWebhooks(): void
    {
        $failedWebhooks = static::canRetry()->get();

        foreach ($failedWebhooks as $webhook) {
            // This would need to be implemented based on your retry logic
            // You might want to store the last payload and retry it
            Log::info("Retrying webhook {$webhook->id}");
        }
    }

    public function getEventLabels(): array
    {
        $labels = [];
        foreach ($this->events as $event) {
            $labels[] = self::EVENTS[$event] ?? $event;
        }
        return $labels;
    }

    public function getStatusLabel(): string
    {
        return match($this->status) {
            'active' => 'Active',
            'inactive' => 'Inactive',
            'failed' => 'Failed',
            default => 'Unknown',
        };
    }

    public function getLastTriggeredFormatted(): ?string
    {
        return $this->last_triggered_at?->diffForHumans();
    }

    public function getLastSuccessFormatted(): ?string
    {
        return $this->last_success_at?->diffForHumans();
    }

    public function getLastFailureFormatted(): ?string
    {
        return $this->last_failure_at?->diffForHumans();
    }

    // Validation
    public function validateUrl(): bool
    {
        return filter_var($this->url, FILTER_VALIDATE_URL) !== false;
    }

    public function validateEvents(): bool
    {
        if (empty($this->events) || !is_array($this->events)) {
            return false;
        }

        foreach ($this->events as $event) {
            if (!array_key_exists($event, self::EVENTS)) {
                return false;
            }
        }

        return true;
    }

    // Boot method
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($webhook) {
            if (empty($webhook->secret)) {
                $webhook->secret = \Str::random(32);
            }
        });
    }
}