<?php
namespace App\Actions;

use Root\Core\Base as BaseClass;
use Root\Core\Helper\Validator;
use App\Models as Models;
use App\Classes\Weather;
use Illuminate\Support\Str;

class GetWeatherCity extends BaseClass\Action
{
    public $province;
    public $cityObj;
    public $weatherData;

    public $city;

    public function AttributesLabel()
    {
        return [
            'city' => AL(0),
        ];
    }

    public function Rules()
    {
        return [
            'city' => Validator::StringVR(),
        ];
    }

    public function Verify(): bool
    {
        if(
            !$this->cityObj = 
                Models\City::
                    where('nameEn', $this->city)
                    ->first()
        )
            return $this->AddRuntimeError('cityNotFound', EL(0));

        $this->province = Models\Province::find($this->cityObj->provinceId);
        
        return true;
    }

    public function Run(): bool | array
    {
        $getSourcesType = config('weather.getSourcesType');
        $result = false;

        switch($getSourcesType) {

            case "crawl":
                $result = $this->HandleCrawlMode();
                break;
            
            case "lastData":
                $result = $this->HandleLastDataMode();
                break;
        }

        if (is_array($result)) {
            $this->AppendOtherCitiesWeather();

            $this->weatherData['provinceName'] = $this->province->nameFa ?? null;

            return $this->weatherData;

        }

        return false;
    }

    private function ProcessHourlyData(): void
    {

        if (!isset($this->weatherData['hourlyData']) || !is_array($this->weatherData['hourlyData'])) {
            return;
        }

        $currentHour = (int)date('G', time() + (3.5 * 3600)); 

        foreach ($this->weatherData['hourlyData'] as &$item) {

            $itemHour = (int)explode(':', $item['time'])[0];
            
            if ($itemHour === $currentHour) {
                $item['isCurrentHour'] = true;
            } else {
                $item['isCurrentHour'] = false;
            }
        }
        
        unset($item);
    }

    private function AppendOtherCitiesWeather(): void
    {

        $popularCityIds = config('weather.popularCityIds') ?? [];

        $filteredCityIds = array_filter($popularCityIds, function($id) {
            return $id != $this->cityObj->id;
        });

        if (empty($filteredCityIds)) {
            $this->weatherData['otherCities'] = [];
            return;
        }

        $cities = Models\City::whereIn('id', $filteredCityIds)->get();
        $provinceIds = $cities->pluck('provinceId')->unique()->toArray();
        $provinces = Models\Province::whereIn('id', $provinceIds)->get()->keyBy('id');

        $weatherResults = Models\WeatherResult::whereIn('cityId', $filteredCityIds)->get()->keyBy('cityId');

        $otherCitiesData = [];
        $otherCitiesCount = config('weather.otherCitiesCount') ?? 4;
        $count = 0;

        foreach ($cities as $city) {

            $count++;
            if($count > $otherCitiesCount) {
                break;
            }

            $temp = "--";
            $type = "unknown";

            if ($weatherResults->has($city->id)) {
                $cityWeatherData = json_decode($weatherResults[$city->id]->data, true);
                $temp = $cityWeatherData['current']['temp'] ?? $cityWeatherData['temp'] ?? "25";
                $type = $cityWeatherData['current']['icon'] ?? $cityWeatherData['type'] ?? "cloud"; 
            }

            $otherCitiesData[] = [
                'name' => $city->nameFa,
                'province' => $provinces->has($city->provinceId) ? $provinces[$city->provinceId]->nameFa : "",
                'slug' => $city->nameEn,
                'temp' => (string)$temp,
                'type' => $type,
            ];
        }

        $this->weatherData['otherCities'] = $otherCitiesData;
    }

    private function HandleCrawlMode(): bool | array
    {
        $weatherResult = Models\WeatherResult::where('cityId', $this->cityObj->id)->first();
        
        if ($this->ShouldCrawl($weatherResult)) {
            return $this->CrawlAndStore();
        }
        
        $this->weatherData = json_decode($weatherResult->data, true);
        return $this->weatherData;
    }

    private function HandleLastDataMode(): bool | array
    {
        $weatherResult = Models\WeatherResult::where('cityId', $this->cityObj->id)->first();
        
        if (!$weatherResult) 
            return $this->AddRuntimeError('weatherDataNotFound', EL(3));
        
        $this->weatherData = json_decode($weatherResult->data, true);
        $this->ProcessHourlyData(); 
        return $this->weatherData;
    }

    private function ShouldCrawl($weatherResult): bool
    {
        if (!$weatherResult) {
            return true;
        }

        $durationCrawlMin = config('weather.durationCrawlMin');
        
        $lastCrawlTimestamp = strtotime($weatherResult->resultDateTime);
        $tehranOffset = 3.5 * 3600; // UTC+3:30
        $lastCrawlTehran = $lastCrawlTimestamp + $tehranOffset;
        $nowTehran = time() + $tehranOffset;
        
        $diffMinutes = ($nowTehran - $lastCrawlTehran) / 60;
        
        return $diffMinutes >= $durationCrawlMin[$this->cityObj->level];
    }

    private function CrawlAndStore(): bool | array
    {
        $activeSource = config('weather.weatherSourcesActive');
        $sourceConfig = config("weather.weatherSources.{$activeSource}");
        
        if (!$sourceConfig || !isset($sourceConfig['classes']))
            return $this->AddRuntimeError('weatherServiceNotFound', EL(1));

        $crawlerClass = $sourceConfig['classes'];
        
        $rawData = $crawlerClass::Crawl($this->cityObj->lat, $this->cityObj->lng);
        
        if ($rawData === false || !$rawData) {
            
            $lastWeatherResult = Models\WeatherResult::where('cityId', $this->cityObj->id)->first();
            
            if ($lastWeatherResult) {
                $this->weatherData = json_decode($lastWeatherResult->data, true);
                $this->ProcessHourlyData(); 
                return $this->weatherData;
            }
            
            return $this->AddRuntimeError('receivingWeatherInformation', El(2));
        }
        
        $processedData = Weather::ProcessWeatherData($rawData);
        
        Models\WeatherResult::updateOrCreate(
            ['cityId' => $this->cityObj->id],
            [
                'id' => Str::uuid(),
                'data' => json_encode($processedData, JSON_UNESCAPED_UNICODE),
                'resultDateTime' => gmdate('Y-m-d H:i:s')
            ]
        );
        
        $this->weatherData = $processedData;
        $this->ProcessHourlyData(); 
        
        return $this->weatherData;
    }
}

 
