<?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 GetWeatherProvince extends BaseClass\Action
{
    public $province;
    public $cities;
    public $weatherData;

    public $provinceSlug;

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

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

    public function Verify(): bool
    {
        if (
            !$this->province =
                Models\Province::where('nameEn', $this->provinceSlug)->first()
        )
            return $this->AddRuntimeError('provinceNotFound', EL(0));

        $this->cities = Models\City::where('provinceId', $this->province->id)
            ->orderBy('level', 'asc')
            ->get();

        if ($this->cities->isEmpty())
            return $this->AddRuntimeError('citiesNotFound', EL(1));

        return true;
    }

    public function Run(): bool | array
    {
        $getSourcesType = 'lastData';
        $weatherData = [];

        switch ($getSourcesType) {
            case "crawl":
                $weatherData = $this->HandleCrawlMode();
                break;
            case "lastData":
                $weatherData = $this->HandleLastDataMode();
                break;
        }

        if ($weatherData === false || empty($weatherData)) {
            return false;
        }

        return $this->FormatResponse($weatherData);
    }

    private function FormatResponse(array $rawData): array
    {
        $locations = [];
        $hasVillages = false;

        foreach ($rawData as $item) {
            $city = $item['city'];
            $weather = $item['weather'];
            
            $type = $hasVillages ? 'village' : 'city';

            $humidity = (int) preg_replace('/[^0-9]/', '', $weather['current']['humidity']);
            $wind = (int) preg_replace('/[^0-9]/', '', $weather['current']['wind']);

            $chartData = array_map(function($hourly) {
                return (int) $hourly['temp'];
            }, array_slice($weather['hourlyData'], 0, 6));

            $locations[] = [
                'id' => $city->id,
                'name' => $city->nameFa,
                'slug' => '/weather/' . $city->nameEn,
                'type' => $type,
                'temp' => (int) $weather['current']['temp'],
                'condition' => $weather['current']['condition'],
                'conditionText' => $weather['current']['type'],
                'humidity' => $humidity,
                'wind' => $wind,
                'aqi' => 0,
                'chartData' => array_values($chartData),
            ];
        }

        return [
            'name' => $this->province->nameFa ?? 'نام استان',
            'slug' => $this->province->nameEn,
            'description' => 'توضیحات مربوط به استان ' . ($this->province->nameFa ?? ''),
            'hasVillages' => $hasVillages,
            'locations' => $locations
        ];
    }

    private function HandleCrawlMode(): bool | array
    {
        $results = [];

        foreach ($this->cities as $city) {
            $weatherResult = Models\WeatherResult::where('cityId', $city->id)->first();

            if ($this->ShouldCrawl($weatherResult, $city)) {
                $data = $this->CrawlAndStore($city);
                if ($data === false) continue;
                $results[] = [
                    'city' => $city,
                    'weather' => $data,
                ];
            } else {
                $results[] = [
                    'city' => $city,
                    'weather' => json_decode($weatherResult->data, true),
                ];
            }
        }

        $this->weatherData = $results;
        return $this->weatherData;
    }

    private function HandleLastDataMode(): bool | array
    {
        $results = [];

        foreach ($this->cities as $city) {
            $weatherResult = Models\WeatherResult::where('cityId', $city->id)->first();

            if (!$weatherResult) continue;

            $results[] = [
                'city' => $city,
                'weather' => json_decode($weatherResult->data, true),
            ];
        }

        if (empty($results))
            return $this->AddRuntimeError('weatherDataNotFound', EL(3));

        $this->weatherData = $results;
        return $this->weatherData;
    }

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

        $durationCrawlMin = config('weather.durationCrawlMin');

        $lastCrawlTimestamp = strtotime($weatherResult->resultDateTime);
        $tehranOffset = 3.5 * 3600;
        $lastCrawlTehran = $lastCrawlTimestamp + $tehranOffset;
        $nowTehran = time() + $tehranOffset;

        $diffMinutes = ($nowTehran - $lastCrawlTehran) / 60;

        return $diffMinutes >= $durationCrawlMin[$city->level];
    }

    private function CrawlAndStore($city): bool | array
    {
        $activeSource = config('weather.weatherSourcesActive');
        $sourceConfig = config("weather.weatherSources.{$activeSource}");

        if (!$sourceConfig || !isset($sourceConfig['classes']))
            return false;

        $crawlerClass = $sourceConfig['classes'];
        $rawData = $crawlerClass::Crawl($city->lat, $city->lng);

        if (!$rawData) return false;

        $processedData = Weather::ProcessWeatherData($rawData);

        Models\WeatherResult::updateOrCreate(
            ['cityId' => $city->id],
            [
                'id' => Str::uuid(),
                'data' => json_encode($processedData, JSON_UNESCAPED_UNICODE),
                'resultDateTime' => gmdate('Y-m-d H:i:s')
            ]
        );

        return $processedData;
    }
}
