<?php

namespace App\Libraries;

use Modules\Offer\Models\OfferDetailsModel;
use Modules\Offer\Models\OfferModel;

/**
 * Shared rules for applying trip-scoped offers (triplist, validation, etc.).
 */
class OfferTripEligibility
{
    /** @var array<int, list<object>> */
    private static array $detailsCache = [];

    public static function findActiveOfferByCode(string $code): ?object
    {
        $code = trim($code);
        if ($code === '') {
            return null;
        }

        $offer = model(OfferModel::class)
            ->where('code', $code)
            ->where('deleted_at', null)
            ->first();

        if (! is_object($offer)) {
            return null;
        }

        if (! OfferModel::isOfferActive($offer->start_date ?? null, $offer->end_date ?? null)) {
            return null;
        }

        return $offer;
    }

    /**
     * @return list<object>
     */
    public static function loadOfferDetails(int $offerId): array
    {
        if (! isset(self::$detailsCache[$offerId])) {
            self::$detailsCache[$offerId] = model(OfferDetailsModel::class)
                ->where('offer_id', $offerId)
                ->where('deleted_at', null)
                ->findAll();
        }

        return self::$detailsCache[$offerId];
    }

    public static function appliesToSubtrip(object $offer, int $mainTripId, int $subtripId, int $companyId): bool
    {
        $details = self::loadOfferDetails((int) $offer->id);
        if ($details === []) {
            return true;
        }

        foreach ($details as $detail) {
            if (self::detailMatches($detail, $mainTripId, $subtripId, $companyId)) {
                return true;
            }
        }

        return false;
    }

    public static function seatClassAllowed(object $offer, string $seatClassId): bool
    {
        $allowed = ! empty($offer->seat_class) ? json_decode($offer->seat_class, true) : [];
        if (! is_array($allowed) || $allowed === []) {
            return true;
        }

        return in_array($seatClassId, array_map('strval', $allowed), true);
    }

    /**
     * @return array{discountAmount: float, finalFare: float}
     */
    public static function calculateSeatFare(object $offer, float $fare): array
    {
        $fare = max(0, $fare);
        $discountType = (int) ($offer->discount_type ?? 0);

        if ($discountType === 1) {
            $discount = min($fare, (float) ($offer->discount ?? 0));

            return [
                'discountAmount' => round($discount, 2),
                'finalFare' => round(max(0, $fare - $discount), 2),
            ];
        }

        if ($discountType === 2) {
            $percent = (float) ($offer->discount ?? 0);
            $discount = round($fare * $percent / 100, 2);

            if ($offer->discount_cap !== null && $offer->discount_cap !== '' && is_numeric($offer->discount_cap)) {
                $discount = min($discount, (float) $offer->discount_cap);
            }
            if ($offer->min_discount !== null && $offer->min_discount !== '' && is_numeric($offer->min_discount) && $discount > 0) {
                $discount = max($discount, (float) $offer->min_discount);
            }
            if ($offer->max_discount !== null && $offer->max_discount !== '' && is_numeric($offer->max_discount)) {
                $discount = min($discount, (float) $offer->max_discount);
            }

            $discount = min($discount, $fare);

            return [
                'discountAmount' => round($discount, 2),
                'finalFare' => round(max(0, $fare - $discount), 2),
            ];
        }

        return [
            'discountAmount' => 0.0,
            'finalFare' => round($fare, 2),
        ];
    }

    private static function detailMatches(object $detail, int $mainTripId, int $subtripId, int $companyId): bool
    {
        if (! self::companyMatches((string) ($detail->company_id ?? ''), $companyId)) {
            return false;
        }

        $subtripIds = json_decode($detail->sub_trip_id ?? '[]', true);
        if (! is_array($subtripIds)) {
            $subtripIds = [];
        }

        $subtripIds = array_values(array_filter(array_map('intval', $subtripIds)));
        $detailMainTrip = (int) ($detail->main_trip_id ?? 0);

        if ($subtripIds !== []) {
            if ($detailMainTrip > 0 && $detailMainTrip !== $mainTripId) {
                return false;
            }

            return in_array($subtripId, $subtripIds, true);
        }

        if ($detailMainTrip > 0) {
            return $detailMainTrip === $mainTripId;
        }

        return true;
    }

    private static function companyMatches(string $companyField, int $companyId): bool
    {
        $companyField = trim($companyField);
        if ($companyField === '' || $companyId <= 0) {
            return true;
        }

        $ids = array_map('intval', array_filter(explode(',', $companyField)));

        return $ids === [] || in_array($companyId, $ids, true);
    }
}
