<?php

namespace App\Exceptions;

use App\Enums\DestinationType;
use Exception;
use Throwable;

class InsuranceCalculationException extends Exception
{
    /**
     * Age not supported
     */
    public static function ageNotSupported(int $age): self
    {
        return new self(
            sprintf('Age %d is not supported. Age must be between 0 and 120 years.', $age),
            400
        );
    }

    /**
     * Duration not supported
     */
    public static function durationNotSupported(int $days): self
    {
        return new self(
            sprintf('Trip duration of %d days is not supported. Duration must be between 1 and 365 days.', $days),
            400
        );
    }

    /**
     * Destination not supported
     */
    public static function destinationNotSupported(string $destination): self
    {
        $validDestinations = implode(', ', array_map(
            fn($case) => $case->value,
            DestinationType::cases()
        ));
        
        return new self(
            sprintf(
                'Destination "%s" is not supported. Valid destinations are: %s',
                $destination,
                $validDestinations
            ),
            400
        );
    }

    /**
     * Invalid date range
     */
    public static function invalidDateRange(string $startDate, string $endDate): self
    {
        return new self(
            sprintf('End date "%s" must be after start date "%s".', $endDate, $startDate),
            400
        );
    }

    /**
     * Missing trip dates
     */
    public static function missingTripDates(): self
    {
        return new self('Trip start and end dates are required.', 400);
    }

    /**
     * Missing destination
     */
    public static function missingDestination(): self
    {
        return new self('Destination is required.', 400);
    }

    /**
     * Missing birthday
     */
    public static function missingBirthday(int $personIndex): self
    {
        return new self(sprintf('Birthday is required for person #%d.', $personIndex), 400);
    }

    /**
     * No people provided
     */
    public static function noPeopleProvided(): self
    {
        return new self('At least one person must be provided for insurance calculation.', 400);
    }

    /**
     * Too many people
     */
    public static function tooManyPeople(int $count): self
    {
        return new self(
            sprintf('Maximum 10 people allowed per inquiry. %d people provided.', $count),
            400
        );
    }

    /**
     * Invalid insurance cover amount
     */
    public static function invalidInsuranceCover(int $amount): self
    {
        return new self(
            sprintf(
                'Insurance cover amount %d is invalid. Amount must be between 1,000 and 1,000,000.',
                $amount
            ),
            400
        );
    }

    /**
     * Invalid deductible amount
     */
    public static function invalidDeductible(int $amount): self
    {
        $validAmounts = [0, 500, 1000, 5000, 10000];
        
        return new self(
            sprintf(
                'Deductible amount %d is invalid. Valid amounts are: %s.',
                $amount,
                implode(', ', $validAmounts)
            ),
            400
        );
    }

    /**
     * Invalid rate category
     */
    public static function invalidRateCategory(string $category): self
    {
        $validCategories = ['A', 'B', 'C'];
        
        return new self(
            sprintf(
                'Rate category "%s" is invalid. Valid categories are: %s.',
                $category,
                implode(', ', $validCategories)
            ),
            400
        );
    }

    /**
     * Rate table not found
     */
    public static function rateTableNotFound(string $tableName, string $company): self
    {
        return new self(
            sprintf('Rate table "%s" not found for company "%s".', $tableName, $company),
            500
        );
    }

    /**
     * Age not covered by plan
     */
    public static function ageNotCovered(int $age, string $planName): self
    {
        return new self(
            sprintf('Age %d is not covered by plan "%s".', $age, $planName),
            400
        );
    }

    /**
     * Duration not covered by plan
     */
    public static function durationNotCovered(int $days, string $planName): self
    {
        return new self(
            sprintf('Trip duration of %d days is not covered by plan "%s".', $days, $planName),
            400
        );
    }

    /**
     * Destination not covered by plan
     */
    public static function destinationNotCovered(string $destination, string $planName): self
    {
        return new self(
            sprintf('Destination "%s" is not covered by plan "%s".', $destination, $planName),
            400
        );
    }

    /**
     * Company not found
     */
    public static function companyNotFound(string $companySlug): self
    {
        return new self(
            sprintf('Insurance company "%s" not found or not active.', $companySlug),
            404
        );
    }

    /**
     * Plan not found
     */
    public static function planNotFound(string $planSlug, string $companySlug): self
    {
        return new self(
            sprintf('Plan "%s" not found for company "%s".', $planSlug, $companySlug),
            404
        );
    }

    /**
     * Rate calculation failed
     */
    public static function rateCalculationFailed(string $reason): self
    {
        return new self(
            sprintf('Rate calculation failed: %s', $reason),
            500
        );
    }

    /**
     * Questionnaire required
     */
    public static function questionnaireRequired(int $age): self
    {
        return new self(
            sprintf('Medical questionnaire is required for age %d+.', $age),
            400
        );
    }

    /**
     * Person ineligible
     */
    public static function personIneligible(int $personIndex, string $reason): self
    {
        return new self(
            sprintf('Person #%d is not eligible for insurance: %s', $personIndex, $reason),
            400
        );
    }

    /**
     * Invalid questionnaire answers
     */
    public static function invalidQuestionnaireAnswers(array $errors): self
    {
        $errorMessages = implode(' ', array_map(
            fn($field, $message) => sprintf('%s: %s', $field, $message),
            array_keys($errors),
            $errors
        ));
        
        return new self(
            sprintf('Invalid questionnaire answers: %s', $errorMessages),
            400
        );
    }

    /**
     * Smoking status required
     */
    public static function smokingStatusRequired(int $age): self
    {
        return new self(
            sprintf('Smoking status is required for age %d+.', $age),
            400
        );
    }

    /**
     * Pre-existing conditions declaration required
     */
    public static function preExistingConditionsRequired(int $age): self
    {
        return new self(
            sprintf('Pre-existing conditions declaration is required for age %d+.', $age),
            400
        );
    }

    /**
     * Trip purpose required
     */
    public static function tripPurposeRequired(): self
    {
        return new self('Trip purpose is required for outbound insurance.', 400);
    }

    /**
     * Invalid trip purpose
     */
    public static function invalidTripPurpose(string $purpose): self
    {
        $validPurposes = ['tourism', 'business', 'education', 'medical', 'other'];
        
        return new self(
            sprintf(
                'Trip purpose "%s" is invalid. Valid purposes are: %s.',
                $purpose,
                implode(', ', $validPurposes)
            ),
            400
        );
    }

    /**
     * Insurance cover not available
     */
    public static function insuranceCoverNotAvailable(int $amount, string $planName): self
    {
        return new self(
            sprintf(
                'Insurance cover amount %d is not available for plan "%s".',
                $amount,
                $planName
            ),
            400
        );
    }

    /**
     * Plan not available for destination
     */
    public static function planNotAvailableForDestination(string $planName, string $destination): self
    {
        return new self(
            sprintf(
                'Plan "%s" is not available for destination "%s".',
                $planName,
                $destination
            ),
            400
        );
    }

    /**
     * Maximum age exceeded for plan
     */
    public static function maximumAgeExceeded(int $age, string $planName, int $maxAge): self
    {
        return new self(
            sprintf(
                'Age %d exceeds maximum age limit of %d for plan "%s".',
                $age,
                $maxAge,
                $planName
            ),
            400
        );
    }

    /**
     * Minimum duration not met
     */
    public static function minimumDurationNotMet(int $days, string $planName, int $minDays): self
    {
        return new self(
            sprintf(
                'Trip duration of %d days is less than minimum %d days required for plan "%s".',
                $days,
                $minDays,
                $planName
            ),
            400
        );
    }

    /**
     * Maximum duration exceeded
     */
    public static function maximumDurationExceeded(int $days, string $planName, int $maxDays): self
    {
        return new self(
            sprintf(
                'Trip duration of %d days exceeds maximum %d days allowed for plan "%s".',
                $days,
                $maxDays,
                $planName
            ),
            400
        );
    }

    /**
     * Create with custom message and code
     */
    public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }

    /**
     * Get error response for API
     */
    public function toArray(): array
    {
        return [
            'error' => true,
            'message' => $this->getMessage(),
            'code' => $this->getCode(),
            'type' => 'insurance_calculation_error',
        ];
    }

    /**
     * Render exception as HTTP response
     */
    public function render($request)
    {
        if ($request->expectsJson()) {
            return response()->json($this->toArray(), $this->getCode());
        }

        return parent::render($request);
    }

    /**
     * Report the exception
     */
    public function report(): void
    {
        // Log the exception with context
        \Log::error('Insurance calculation error', [
            'message' => $this->getMessage(),
            'code' => $this->getCode(),
            'file' => $this->getFile(),
            'line' => $this->getLine(),
            'trace' => $this->getTraceAsString(),
        ]);
    }
}