<?php

declare(strict_types=1);

namespace App\Framework\Waf\Feedback;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\DateTime\Clock;
use App\Framework\Logging\Logger;
use App\Framework\Waf\DetectionCategory;
use App\Framework\Waf\DetectionSeverity;
use App\Framework\Waf\ValueObjects\Detection;

/**
 * Service for handling WAF feedback submission and retrieval
 */
final readonly class FeedbackService
{
    /**
     * @param FeedbackRepositoryInterface $repository Repository for storing and retrieving feedback
     * @param Clock $clock Clock for getting current time
     * @param Logger $logger Logger for logging feedback events
     */
    public function __construct(
        private FeedbackRepositoryInterface $repository,
        private Clock $clock,
        private Logger $logger
    ) {
    }

    /**
     * Submit feedback for a false positive detection
     *
     * @param string $detectionId ID of the detection
     * @param string $userId ID of the user submitting the feedback
     * @param string|null $comment Optional comment
     * @param DetectionCategory $category Category of the detection
     * @param DetectionSeverity $severity Severity of the detection
     * @param array<string, mixed> $context Additional context information
     * @return DetectionFeedback The created feedback
     */
    public function submitFalsePositive(
        string $detectionId,
        string $userId,
        ?string $comment,
        DetectionCategory $category,
        DetectionSeverity $severity,
        array $context = []
    ): DetectionFeedback {
        $feedback = DetectionFeedback::falsePositive(
            $detectionId,
            $userId,
            $comment,
            $category,
            $severity,
            $context
        );

        $this->repository->saveFeedback($feedback);

        $this->logger->info('WAF false positive feedback submitted', [
            'detection_id' => $detectionId,
            'user_id' => $userId,
            'category' => $category->value,
            'severity' => $severity->value,
        ]);

        return $feedback;
    }

    /**
     * Submit feedback for a false negative detection
     *
     * @param string $detectionId ID of the detection
     * @param string $userId ID of the user submitting the feedback
     * @param string|null $comment Optional comment
     * @param DetectionCategory $category Category of the detection
     * @param DetectionSeverity $severity Severity of the detection
     * @param array<string, mixed> $context Additional context information
     * @return DetectionFeedback The created feedback
     */
    public function submitFalseNegative(
        string $detectionId,
        string $userId,
        ?string $comment,
        DetectionCategory $category,
        DetectionSeverity $severity,
        array $context = []
    ): DetectionFeedback {
        $feedback = DetectionFeedback::falseNegative(
            $detectionId,
            $userId,
            $comment,
            $category,
            $severity,
            $context
        );

        $this->repository->saveFeedback($feedback);

        $this->logger->info('WAF false negative feedback submitted', [
            'detection_id' => $detectionId,
            'user_id' => $userId,
            'category' => $category->value,
            'severity' => $severity->value,
        ]);

        return $feedback;
    }

    /**
     * Submit feedback for a correct detection
     *
     * @param string $detectionId ID of the detection
     * @param string $userId ID of the user submitting the feedback
     * @param string|null $comment Optional comment
     * @param DetectionCategory $category Category of the detection
     * @param DetectionSeverity $severity Severity of the detection
     * @param array<string, mixed> $context Additional context information
     * @return DetectionFeedback The created feedback
     */
    public function submitCorrectDetection(
        string $detectionId,
        string $userId,
        ?string $comment,
        DetectionCategory $category,
        DetectionSeverity $severity,
        array $context = []
    ): DetectionFeedback {
        $feedback = DetectionFeedback::correctDetection(
            $detectionId,
            $userId,
            $comment,
            $category,
            $severity,
            $context
        );

        $this->repository->saveFeedback($feedback);

        $this->logger->info('WAF correct detection feedback submitted', [
            'detection_id' => $detectionId,
            'user_id' => $userId,
            'category' => $category->value,
            'severity' => $severity->value,
        ]);

        return $feedback;
    }

    /**
     * Submit feedback for a severity adjustment
     *
     * @param string $detectionId ID of the detection
     * @param string $userId ID of the user submitting the feedback
     * @param string|null $comment Optional comment
     * @param DetectionCategory $category Category of the detection
     * @param DetectionSeverity $currentSeverity Current severity of the detection
     * @param DetectionSeverity $suggestedSeverity Suggested severity of the detection
     * @param array<string, mixed> $context Additional context information
     * @return DetectionFeedback The created feedback
     */
    public function submitSeverityAdjustment(
        string $detectionId,
        string $userId,
        ?string $comment,
        DetectionCategory $category,
        DetectionSeverity $currentSeverity,
        DetectionSeverity $suggestedSeverity,
        array $context = []
    ): DetectionFeedback {
        $feedback = DetectionFeedback::severityAdjustment(
            $detectionId,
            $userId,
            $comment,
            $category,
            $currentSeverity,
            $suggestedSeverity,
            $context
        );

        $this->repository->saveFeedback($feedback);

        $this->logger->info('WAF severity adjustment feedback submitted', [
            'detection_id' => $detectionId,
            'user_id' => $userId,
            'category' => $category->value,
            'current_severity' => $currentSeverity->value,
            'suggested_severity' => $suggestedSeverity->value,
        ]);

        return $feedback;
    }

    /**
     * Submit feedback for a detection
     *
     * @param Detection $detection The detection to submit feedback for
     * @param FeedbackType $feedbackType Type of feedback
     * @param string $userId ID of the user submitting the feedback
     * @param string|null $comment Optional comment
     * @param array<string, mixed> $context Additional context information
     * @return DetectionFeedback The created feedback
     */
    public function submitFeedbackForDetection(
        Detection $detection,
        FeedbackType $feedbackType,
        string $userId,
        ?string $comment = null,
        array $context = []
    ): DetectionFeedback {
        $mergedContext = array_merge($context, [
            'detection_context' => $detection->context,
            'detection_timestamp' => $detection->timestamp?->toFloat(),
            'detection_source' => $detection->source,
        ]);

        return match ($feedbackType) {
            FeedbackType::FALSE_POSITIVE => $this->submitFalsePositive(
                $detection->id,
                $userId,
                $comment,
                $detection->category,
                $detection->severity,
                $mergedContext
            ),
            FeedbackType::FALSE_NEGATIVE => $this->submitFalseNegative(
                $detection->id,
                $userId,
                $comment,
                $detection->category,
                $detection->severity,
                $mergedContext
            ),
            FeedbackType::CORRECT_DETECTION => $this->submitCorrectDetection(
                $detection->id,
                $userId,
                $comment,
                $detection->category,
                $detection->severity,
                $mergedContext
            ),
            FeedbackType::SEVERITY_ADJUSTMENT => $this->submitSeverityAdjustment(
                $detection->id,
                $userId,
                $comment,
                $detection->category,
                $detection->severity,
                // Default to MEDIUM if not specified in context
                DetectionSeverity::from($context['suggested_severity'] ?? DetectionSeverity::MEDIUM->value),
                $mergedContext
            ),
        };
    }

    /**
     * Get feedback for a specific detection
     *
     * @param string $detectionId ID of the detection
     * @return DetectionFeedback[] Array of feedback for the detection
     */
    public function getFeedbackForDetection(string $detectionId): array
    {
        return $this->repository->getFeedbackForDetection($detectionId);
    }

    /**
     * Get feedback by category
     *
     * @param DetectionCategory $category Category to filter by
     * @param Timestamp|null $since Optional timestamp to filter feedback after a certain date
     * @return DetectionFeedback[] Array of feedback for the category
     */
    public function getFeedbackByCategory(DetectionCategory $category, ?Timestamp $since = null): array
    {
        return $this->repository->getFeedbackByCategory($category, $since);
    }

    /**
     * Get feedback by feedback type
     *
     * @param FeedbackType $feedbackType Feedback type to filter by
     * @param Timestamp|null $since Optional timestamp to filter feedback after a certain date
     * @return DetectionFeedback[] Array of feedback for the feedback type
     */
    public function getFeedbackByFeedbackType(FeedbackType $feedbackType, ?Timestamp $since = null): array
    {
        return $this->repository->getFeedbackByFeedbackType($feedbackType, $since);
    }

    /**
     * Get feedback statistics
     *
     * @return array<string, mixed> Statistics about the feedback
     */
    public function getFeedbackStats(): array
    {
        return $this->repository->getFeedbackStats();
    }

    /**
     * Get recent feedback
     *
     * @param int $limit Maximum number of entries to return
     * @return DetectionFeedback[] Array of recent feedback
     */
    public function getRecentFeedback(int $limit = 10): array
    {
        return $this->repository->getRecentFeedback($limit);
    }
}
