<?php

namespace App\Http\Livewire\MainPage;

use App\Http\Livewire\Calls\CallCarouselScorer;
use App\Http\Livewire\Calls\ProfileCalls;
use App\Models\Call;
use App\Models\Locations\City;
use App\Models\Locations\Country;
use App\Models\Locations\Division;
use App\Models\Locations\Location;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;

class CallCardCarousel extends Component
{
    public array $calls       = [];
    public bool  $related     = true;
    public bool  $random      = false;
    public int   $maxCards    = 0;
    public bool  $showScore   = false;
    private const UNKNOWN_COUNTRY_ID = 10;

    public function mount(bool $related, bool $random = false, int $maxCards = 0): void
    {
        $this->related  = $related;
        $this->random   = $random;
        $this->maxCards = $maxCards;

        $carouselCfg    = timebank_config('calls.carousel', []);
        $this->maxCards = (int) ($carouselCfg['max_cards'] ?? ($maxCards ?: 12));
        $locale         = App::getLocale();

        // --- Resolve active profile and its location ---
        $profile = getActiveProfile();
        if ($profile) {
            \App\Helpers\ProfileAuthorizationHelper::authorize($profile);
        }

        $location          = $profile?->locations ? $profile->locations()->first() : null;
        $profileCityId     = $location ? ($location->city_id     ?? $location->city?->id)     : null;
        $profileDivisionId = $location ? ($location->division_id ?? $location->division?->id) : null;
        $profileCountryId  = $location ? ($location->country_id  ?? $location->country?->id)  : null;

        // Expand location IDs based on $related flag (same sibling logic as EventCardFull)
        $locationCityIds     = [];
        $locationDivisionIds = [];
        $locationCountryIds  = [];

        if ($location) {
            if (!$location->division && !$location->city) {
                $country = Location::find($location->id)->country;
                $locationCountryIds = $related
                    ? Country::pluck('id')->toArray()
                    : ($country ? [$country->id] : []);
            } elseif ($location->division && !$location->city) {
                $divisionId = Location::find($location->id)->division->id;
                $locationDivisionIds = $related
                    ? Division::find($divisionId)->parent->divisions->pluck('id')->toArray()
                    : [$divisionId];
            } elseif ($location->city) {
                $cityId = Location::find($location->id)->city->id;
                $locationCityIds = $related
                    ? City::find($cityId)->parent->cities->pluck('id')->toArray()
                    : [$cityId];
            }
        }

        // --- Base query — safety exclusions are always hardcoded ---
        $query = Call::with([
                'tag.contexts.category.translations',
                'tag.contexts.category.ancestors.translations',
                'translations',
                'location.city.translations',
                'location.country.translations',
                'callable.locations.city.translations',
                'callable.locations.division.translations',
                'callable.locations.country.translations',
                'callable.loveReactant.reactionCounters',
                'loveReactant.reactionCounters',
            ])
            ->whereNull('deleted_at')
            ->where('is_paused', false)
            ->where('is_suppressed', false)
            ->where(fn ($q) => $q->whereNull('till')->orWhere('till', '>=', now()));

        // Enforce is_public for guests or when config requires it
        if (!Auth::check() || ($carouselCfg['exclude_non_public'] ?? true)) {
            $query->where('is_public', true);
        }

        // Configurable: exclude the active profile's own calls
        if ($profile && ($carouselCfg['exclude_own_calls'] ?? true)) {
            $query->where(function ($q) use ($profile) {
                $q->where('callable_type', '!=', get_class($profile))
                  ->orWhere('callable_id', '!=', $profile->id);
            });
        }

        // --- Locality filter ---
        $includeUnknown  = $carouselCfg['include_unknown_location'] ?? true;
        $includeDivision = $carouselCfg['include_same_division']    ?? true;
        $includeCountry  = $carouselCfg['include_same_country']     ?? true;

        $hasLocalityFilter = $locationCityIds || $locationDivisionIds || $locationCountryIds
            || ($includeDivision && $locationDivisionIds)
            || ($includeCountry  && $locationCountryIds)
            || $includeUnknown;

        if ($hasLocalityFilter) {
            $query->where(function ($q) use (
                $locationCityIds, $locationDivisionIds, $locationCountryIds,
                $includeDivision, $includeCountry, $includeUnknown
            ) {
                // Always include calls matching the profile's city
                if ($locationCityIds) {
                    $q->orWhereHas('location', fn ($lq) => $lq->whereIn('city_id', $locationCityIds));
                }
                if ($includeDivision && $locationDivisionIds) {
                    $q->orWhereHas('location', fn ($lq) => $lq->whereIn('division_id', $locationDivisionIds));
                }
                if ($includeCountry && $locationCountryIds) {
                    $q->orWhereHas('location', fn ($lq) => $lq->whereIn('country_id', $locationCountryIds));
                }
                if ($includeUnknown) {
                    $q->orWhereHas('location', fn ($lq) => $lq->where('country_id', self::UNKNOWN_COUNTRY_ID));
                }
            });
        }

        // Fetch pool for scoring
        $isAdmin = getActiveProfileType() === 'Admin';
        $this->showScore = (bool) ($carouselCfg['show_score'] ?? false)
            || ($isAdmin && (bool) ($carouselCfg['show_score_for_admins'] ?? true));
        $poolSize        = $this->maxCards * max(1, (int) ($carouselCfg['pool_multiplier'] ?? 5));
        $calls    = $query->limit($poolSize)->get();

        // --- Score, sort, take top $maxCards ---
        $scorer = new CallCarouselScorer(
            $carouselCfg,
            $profileCityId,
            $profileDivisionId,
            $profileCountryId
        );

        $this->calls = $calls->map(function (Call $call) use ($locale, $scorer) {
            $translation = $call->translations->firstWhere('locale', $locale)
                ?? $call->translations->first();

            $tag         = $call->tag;
            $tagContext  = $tag?->contexts->first();
            $tagCategory = $tagContext?->category;
            $tagColor    = $tagCategory?->relatedColor ?? 'gray';
            $tagName     = $tag?->translation?->name ?? $tag?->name;

            $locationStr = null;
            if ($call->location) {
                $loc   = $call->location;
                $parts = [];
                if ($loc->city) {
                    $cityName = optional($loc->city->translations->first())->name;
                    if ($cityName) $parts[] = $cityName;
                }
                if ($loc->country) {
                    if ($loc->country->code === 'XX') {
                        $parts[] = __('Location not specified');
                    } elseif ($loc->country->code) {
                        $parts[] = strtoupper($loc->country->code);
                    }
                }
                $locationStr = $parts ? implode(', ', $parts) : null;
            }

            $tagCategories = [];
            if ($tagCategory) {
                $ancestors = $tagCategory->ancestorsAndSelf()->get()->reverse();
                foreach ($ancestors as $cat) {
                    $catName = $cat->translations->firstWhere('locale', $locale)?->name
                        ?? $cat->translations->first()?->name
                        ?? '';
                    if ($catName) {
                        $tagCategories[] = [
                            'name'  => $catName,
                            'color' => $cat->relatedColor ?? 'gray',
                        ];
                    }
                }
            }

            $score = $scorer->score($call);

            // Add random jitter when $random=true to vary order while preserving scoring preference
            if ($this->random) {
                $score *= (random_int(85, 115) / 100);
            }

            return [
                'id'                => $call->id,
                'model'             => Call::class,
                'title'             => $tagName ?? '',
                'excerpt'           => $translation?->content ?? '',
                'photo'             => $call->callable?->profile_photo_url ?? '',
                'location'          => $locationStr,
                'tag_color'         => $tagColor,
                'tag_categories'    => $tagCategories,
                'callable_name'     => $call->callable?->name ?? '',
                'callable_location' => ProfileCalls::buildCallableLocation($call->callable),
                'till'              => $call->till,
                'expiry_badge_text' => ProfileCalls::buildExpiryBadgeText($call->till),
                'like_count'        => $call->loveReactant?->reactionCounters
                    ->firstWhere('reaction_type_id', 3)?->count ?? 0,
                'score'             => $score,
            ];
        })
        ->sortByDesc('score')
        ->take($this->maxCards)
        ->values()
        ->toArray();
    }

    public function render()
    {
        return view('livewire.main-page.call-card-carousel');
    }
}
