<?php

namespace App\Livewire;

use App\Models\Park;
use App\Models\Operator;
use Livewire\Component;
use Livewire\WithPagination;
use Livewire\Attributes\Url;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Builder;

class ParksListing extends Component
{
    use WithPagination;

    // Search and Filter Properties with URL binding
    #[Url(as: 'q')]
    public string $search = '';

    #[Url(as: 'operator')]
    public string $operatorId = '';

    #[Url(as: 'region')]
    public string $region = '';

    #[Url(as: 'country')]
    public string $country = '';

    #[Url(as: 'type')]
    public string $parkType = '';

    #[Url(as: 'year_from')]
    public string $openingYearFrom = '';

    #[Url(as: 'year_to')]
    public string $openingYearTo = '';

    #[Url(as: 'min_area')]
    public string $minArea = '';

    #[Url(as: 'max_area')]
    public string $maxArea = '';

    #[Url(as: 'min_rides')]
    public string $minRides = '';

    #[Url(as: 'max_distance')]
    public string $maxDistance = '';

    #[Url(as: 'sort')]
    public string $sortBy = 'name';

    #[Url(as: 'dir')]
    public string $sortDirection = 'asc';

    // Location Properties
    public ?array $userLocation = null;
    public bool $locationEnabled = false;
    public bool $locationLoading = false;

    // UI State
    public bool $showFilters = false;
    public bool $isLoading = false;

    /**
     * Component initialization
     */
    public function mount(): void
    {
        // Initialize component state
        $this->resetPage();
    }

    /**
     * Updated hook for reactive properties
     */
    public function updated($property): void
    {
        if (in_array($property, [
            'search', 'operatorId', 'region', 'country', 'parkType',
            'openingYearFrom', 'openingYearTo', 'minArea', 'maxArea',
            'minRides', 'maxDistance', 'sortBy', 'sortDirection'
        ])) {
            $this->resetPage();
            $this->invalidateCache('parks');
        }
    }

    /**
     * Enable location services
     */
    public function enableLocation(): void
    {
        $this->locationLoading = true;
        $this->dispatch('request-location');
    }

    /**
     * Handle location received from JavaScript
     */
    public function locationReceived(array $location): void
    {
        $this->userLocation = $location;
        $this->locationEnabled = true;
        $this->locationLoading = false;
        $this->resetPage();
        $this->invalidateCache('parks');
    }

    /**
     * Handle location error
     */
    public function locationError(string $error): void
    {
        $this->locationLoading = false;
        $this->dispatch('location-error', message: $error);
    }

    /**
     * Clear all filters
     */
    public function clearFilters(): void
    {
        $this->reset([
            'search', 'operatorId', 'region', 'country', 'parkType',
            'openingYearFrom', 'openingYearTo', 'minArea', 'maxArea',
            'minRides', 'maxDistance'
        ]);
        $this->userLocation = null;
        $this->locationEnabled = false;
        $this->resetPage();
        $this->invalidateCache('parks');
    }

    /**
     * Toggle filters visibility
     */
    public function toggleFilters(): void
    {
        $this->showFilters = !$this->showFilters;
    }

    /**
     * Sort parks by given field
     */
    public function sortBy(string $field): void
    {
        if ($this->sortBy === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortBy = $field;
            $this->sortDirection = 'asc';
        }
        $this->resetPage();
        $this->invalidateCache('parks');
    }

    /**
     * Get parks with location-aware search and filtering
     */
    public function getParksProperty()
    {
        return $this->remember('parks', function () {
            return $this->buildParksQuery()
                ->paginate(12, ['*'], 'page', $this->getPage());
        }, 1200); // 20-minute location-aware caching
    }

    /**
     * Build the parks query with Django parity search and filtering
     */
    protected function buildParksQuery(): Builder
    {
        $query = Park::query();

        // Location-aware search functionality (Django parity)
        if (!empty($this->search)) {
            $query = $this->locationAwareSearch($query, $this->search, $this->userLocation);
        }

        // Apply advanced filters with geographic context
        $query = $this->applyFilters($query, [
            'operator_id' => $this->operatorId,
            'region' => $this->region,
            'country' => $this->country,
            'park_type' => $this->parkType,
            'opening_year_from' => $this->openingYearFrom,
            'opening_year_to' => $this->openingYearTo,
            'min_area' => $this->minArea,
            'max_area' => $this->maxArea,
            'min_rides' => $this->minRides,
            'max_distance' => $this->maxDistance,
        ], $this->userLocation);

        // Apply sorting with location-aware options
        $query = $this->applySorting($query);

        // Eager load relationships for performance
        $query->with(['location', 'operator', 'photos', 'statistics'])
             ->withCount(['rides', 'reviews']);

        return $query;
    }

    /**
     * Location-aware search functionality matching Django implementation
     */
    protected function locationAwareSearch(Builder $query, string $searchQuery, ?array $userLocation = null): Builder
    {
        $terms = explode(' ', trim($searchQuery));
        
        foreach ($terms as $term) {
            $term = trim($term);
            if (empty($term)) continue;

            $query->where(function ($subQuery) use ($term) {
                $subQuery->where('name', 'ilike', "%{$term}%")
                        ->orWhere('description', 'ilike', "%{$term}%")
                        ->orWhere('park_type', 'ilike', "%{$term}%")
                        ->orWhereHas('location', function($locQuery) use ($term) {
                            $locQuery->where('city', 'ilike', "%{$term}%")
                                    ->orWhere('state', 'ilike', "%{$term}%")
                                    ->orWhere('country', 'ilike', "%{$term}%");
                        })
                        ->orWhereHas('operator', fn($opQuery) => 
                            $opQuery->where('name', 'ilike', "%{$term}%"));
            });
        }

        // Add distance-based ordering for location-aware results
        if ($userLocation) {
            $query->selectRaw('parks.*, 
                (6371 * acos(cos(radians(?)) * cos(radians(locations.latitude)) * 
                cos(radians(locations.longitude) - radians(?)) + 
                sin(radians(?)) * sin(radians(locations.latitude)))) AS distance', 
                [$userLocation['lat'], $userLocation['lng'], $userLocation['lat']])
              ->join('locations', 'parks.location_id', '=', 'locations.id')
              ->orderBy('distance');
        }

        return $query;
    }

    /**
     * Apply advanced filters with geographic context
     */
    protected function applyFilters(Builder $query, array $filters, ?array $userLocation = null): Builder
    {
        return $query
            ->when($filters['operator_id'] ?? null, fn($q, $operatorId) => 
                $q->where('operator_id', $operatorId))
            ->when($filters['region'] ?? null, fn($q, $region) => 
                $q->whereHas('location', fn($locQ) => $locQ->where('state', $region)))
            ->when($filters['country'] ?? null, fn($q, $country) => 
                $q->whereHas('location', fn($locQ) => $locQ->where('country', $country)))
            ->when($filters['park_type'] ?? null, fn($q, $type) => 
                $q->where('park_type', $type))
            ->when($filters['opening_year_from'] ?? null, fn($q, $year) => 
                $q->where('opening_date', '>=', "{$year}-01-01"))
            ->when($filters['opening_year_to'] ?? null, fn($q, $year) => 
                $q->where('opening_date', '<=', "{$year}-12-31"))
            ->when($filters['min_area'] ?? null, fn($q, $area) => 
                $q->where('area_acres', '>=', $area))
            ->when($filters['max_area'] ?? null, fn($q, $area) => 
                $q->where('area_acres', '<=', $area))
            ->when($filters['min_rides'] ?? null, fn($q, $count) => 
                $q->whereHas('rides', fn($rideQ) => $rideQ->havingRaw('COUNT(*) >= ?', [$count])))
            ->when($filters['max_distance'] ?? null && $userLocation, function($q) use ($filters, $userLocation) {
                $q->whereRaw('(6371 * acos(cos(radians(?)) * cos(radians(locations.latitude)) * 
                    cos(radians(locations.longitude) - radians(?)) + 
                    sin(radians(?)) * sin(radians(locations.latitude)))) <= ?', 
                    [$userLocation['lat'], $userLocation['lng'], $userLocation['lat'], $filters['max_distance']]);
            });
    }

    /**
     * Apply sorting with location-aware options
     */
    protected function applySorting(Builder $query): Builder
    {
        switch ($this->sortBy) {
            case 'distance':
                if ($this->userLocation) {
                    // Distance sorting already applied in locationAwareSearch
                    return $query;
                }
                // Fallback to name if no location
                return $query->orderBy('name', $this->sortDirection);
            
            case 'rides_count':
                return $query->orderBy('rides_count', $this->sortDirection);
            
            case 'reviews_count':
                return $query->orderBy('reviews_count', $this->sortDirection);
            
            case 'opening_date':
                return $query->orderBy('opening_date', $this->sortDirection);
            
            case 'area_acres':
                return $query->orderBy('area_acres', $this->sortDirection);
            
            case 'name':
            default:
                return $query->orderBy('name', $this->sortDirection);
        }
    }

    /**
     * Get available operators for filter dropdown
     */
    public function getOperatorsProperty()
    {
        return $this->remember('operators', function () {
            return Operator::select('id', 'name')
                ->whereHas('parks')
                ->orderBy('name')
                ->get();
        }, 3600);
    }

    /**
     * Get available regions for filter dropdown
     */
    public function getRegionsProperty()
    {
        return $this->remember('regions', function () {
            return DB::table('locations')
                ->join('parks', 'locations.id', '=', 'parks.location_id')
                ->select('locations.state')
                ->distinct()
                ->whereNotNull('locations.state')
                ->orderBy('locations.state')
                ->pluck('state');
        }, 3600);
    }

    /**
     * Get available countries for filter dropdown
     */
    public function getCountriesProperty()
    {
        return $this->remember('countries', function () {
            return DB::table('locations')
                ->join('parks', 'locations.id', '=', 'parks.location_id')
                ->select('locations.country')
                ->distinct()
                ->whereNotNull('locations.country')
                ->orderBy('locations.country')
                ->pluck('country');
        }, 3600);
    }

    /**
     * Get available park types for filter dropdown
     */
    public function getParkTypesProperty()
    {
        return $this->remember('park_types', function () {
            return Park::select('park_type')
                ->distinct()
                ->whereNotNull('park_type')
                ->orderBy('park_type')
                ->pluck('park_type');
        }, 3600);
    }

    /**
     * Get filter summary for display
     */
    public function getActiveFiltersProperty(): array
    {
        $filters = [];
        
        if (!empty($this->search)) {
            $filters[] = "Search: {$this->search}";
        }
        
        if (!empty($this->operatorId)) {
            $operator = $this->operators->find($this->operatorId);
            if ($operator) {
                $filters[] = "Operator: {$operator->name}";
            }
        }
        
        if (!empty($this->region)) {
            $filters[] = "Region: {$this->region}";
        }
        
        if (!empty($this->country)) {
            $filters[] = "Country: {$this->country}";
        }
        
        if (!empty($this->parkType)) {
            $filters[] = "Type: {$this->parkType}";
        }
        
        if (!empty($this->openingYearFrom) || !empty($this->openingYearTo)) {
            $yearRange = $this->openingYearFrom . ' - ' . $this->openingYearTo;
            $filters[] = "Years: {$yearRange}";
        }
        
        if (!empty($this->minArea) || !empty($this->maxArea)) {
            $areaRange = $this->minArea . ' - ' . $this->maxArea . ' acres';
            $filters[] = "Area: {$areaRange}";
        }
        
        if (!empty($this->minRides)) {
            $filters[] = "Min Rides: {$this->minRides}";
        }
        
        if (!empty($this->maxDistance) && $this->locationEnabled) {
            $filters[] = "Within: {$this->maxDistance} km";
        }
        
        return $filters;
    }

    /**
     * Render the component
     */
    public function render()
    {
        $this->isLoading = false;
        
        return view('livewire.parks-listing', [
            'parks' => $this->parks,
            'operators' => $this->operators,
            'regions' => $this->regions,
            'countries' => $this->countries,
            'parkTypes' => $this->parkTypes,
            'activeFilters' => $this->activeFilters,
        ]);
    }

    /**
     * Get cache key for this component
     */
    protected function getCacheKey(string $suffix = ''): string
    {
        $locationKey = $this->userLocation ? 
            md5(json_encode($this->userLocation)) : 'no-location';
        
        $filterKey = md5(serialize([
            $this->search, $this->operatorId, $this->region, $this->country,
            $this->parkType, $this->openingYearFrom, $this->openingYearTo,
            $this->minArea, $this->maxArea, $this->minRides, $this->maxDistance,
            $this->sortBy, $this->sortDirection
        ]));
        
        return 'thrillwiki.' . class_basename(static::class) . '.' . 
               $locationKey . '.' . $filterKey . '.' . $suffix;
    }

    /**
     * Remember data with caching
     */
    protected function remember(string $key, $callback, int $ttl = 3600)
    {
        return Cache::remember($this->getCacheKey($key), $ttl, $callback);
    }

    /**
     * Invalidate component cache
     */
    protected function invalidateCache(string $key = null): void
    {
        if ($key) {
            Cache::forget($this->getCacheKey($key));
        } else {
            // Clear all cache for this component
            Cache::flush();
        }
    }
}