<?php

namespace App\Services;

use App\Models\Backoffice\WebsiteObject;
use App\Models\Backoffice\WebsiteObjectLanguage;
use App\Models\City;
use App\Models\CityLanguage;
use App\Models\Country;
use App\Models\Nuts3Region;
use App\Models\Nuts3RegionLanguage;
use App\Models\State;
use App\Models\StateLanguage;
use Illuminate\Support\Facades\Storage;
use Shapefile\ShapefileException;
use Shapefile\ShapefileReader;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use MatanYadaev\EloquentSpatial\Objects\Geometry as ObjectsGeometry;
use MatanYadaev\EloquentSpatial\Objects\Point;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Shapefile\Geometry\Geometry;
use Shapefile\Shapefile;
use Throwable;

class LocalizationService
{
    protected array $languages;

    public function __construct()
    {
        $this->languages = LaravelLocalization::getSupportedLanguagesKeys();
    }

    /**
     * Get Countries
     *
     * @param array $filter
     * @param int $perPage
     * @return mixed
     */
    public function getCountries(array $filter = [], int $perPage = 20, $showSpecialCountries = false)
    {
        $countries = Country::full();

        if (!empty($filter['iso2'])) {
            $countries->where('iso2', $filter['iso2']);
        }

        if (!empty($filter['iso3'])) {
            $countries->where('iso3', $filter['iso3']);
        }

        if (!empty($filter['commonName'])) {
            $countries->where('common_name', 'like', '%' . $filter['commonName'] . '%');
        }

        if (!empty($filter['name'])) {
            $countries->where('title', 'like', '%' . $filter['name'] . '%');
        }

        /*
        countries to show first by ID:
            Germany: 61
            Austria: 16,
            Switzerland: 43
        */

        if (!empty($showSpecialCountries)) {
            $countryIds = [16, 43, 61];
            $ids = implode(',', $countryIds);

            $countries->orderByRaw("FIELD(id, $ids) DESC");
        }

        return $countries->orderBy('common_name', 'asc')->paginate($perPage);
    }

    /**
     * Get States
     *
     * @param array $filter
     * @param int $perPage
     * @return mixed
     */
    public function getStates(array $filter = [], int $perPage = 20)
    {
        $states = State::full()->with(['country']);

        if (!empty($filter['code'])) {
            $states->where('code', $filter['code']);
        }

        if (!empty($filter['name'])) {
            $states->where('title', 'like', '%' . $filter['name'] . '%');
        }

        if (!empty($filter['countryId'])) {
            $states->where('country_id', $filter['countryId']);
        }

        return $states->orderBy('title', 'asc')->paginate($perPage);
    }

    /**
     * Get Cities
     *
     * @param array $filter
     * @param int $perPage
     * @return mixed
     */
    public function getCities(array $filter = [], int $perPage = 20)
    {
        $cities = City::full()->with(['state', 'state.country']);

        if (!empty($filter['name'])) {
            $cities->where('title', 'like', '%' . $filter['name'] . '%');
        }

        if (!empty($filter['stateId'])) {
            $cities->where('state_id', $filter['stateId']);
        }

        return $cities->orderBy('title', 'asc')->paginate($perPage);
    }

    /**
     * Import NUTS codes from Shapefile
     *
     * @return void
     */
    public function importNutsCodes()
    {
        try {
            // Open Shapefile
            $Shapefile = new ShapefileReader(Storage::path('import/NUTS_RG_20M_2024_4326.shp'), [
                Shapefile::OPTION_DBF_CONVERT_TO_UTF8 => false,
            ]);

            // Read all the records
            while ($geoNuts = $Shapefile->fetchRecord()) {
                // Skip the record if marked as "deleted"
                if ($geoNuts->isDeleted()) {
                    continue;
                }

                // Print DBF data
                print_r($geoNuts->getDataArray());

                $this->importNutsCode($geoNuts);
            }

        } catch (ShapefileException $e) {
            // Print detailed error information
            echo "Error Type: " . $e->getErrorType()
                . "\nMessage: " . $e->getMessage()
                . "\nDetails: " . $e->getDetails();
        }
    }

    /**
     * Import NUTS code
     *
     * @param Geometry $nutsGeo
     * @return void
     */
    protected function importNutsCode(Geometry $nutsGeo)
    {
        $codeData = $nutsGeo->getDataArray();

        // Check if the code is NUTS3
        if ($codeData['LEVL_CODE'] != 3) {
            return;
        }

        DB::beginTransaction();
        try {
            $isNew = false;
            $nutsRegion = Nuts3Region::where('code', $codeData['NUTS_ID'])->first();

            if (!$nutsRegion) {
                $nutsRegion = new Nuts3Region();
                $isNew = true;
            }

            $nutsRegion->shape = ObjectsGeometry::fromWkt($nutsGeo->getWKT());
            $nutsRegion->code = $codeData['NUTS_ID'];
            $nutsRegion->native_name = $codeData['NUTS_NAME'];
            $nutsRegion->save();

            $out = DB::table('nuts3_regions')
                ->where('id', $nutsRegion->id)
                ->selectRaw('id,  ST_X(ST_Centroid(shape)) as x,  ST_Y(ST_Centroid(shape)) as y')
                ->first();

            $nutsRegion->location = new Point($out->y, $out->x);
            $nutsRegion->location_zoom = 8;
            $nutsRegion->save();

            if ($isNew) {
                $wo = new WebsiteObject();
                $wo->object_id = $nutsRegion->id;
                $wo->object_type = Nuts3Region::class;
                $wo->content_type = 'nuts3_region';
                $wo->save();
            }

            foreach ($this->languages as $language) {
                $nutsRegionLang = $nutsRegion->languages()->where('language_id', $language)->first();

                if (!$nutsRegionLang) {
                    $nutsRegionLang = new Nuts3RegionLanguage();
                    $nutsRegionLang->language_id = $language;
                    $nutsRegionLang->nuts3_region_id = $nutsRegion->id;
                }

                $nutsRegionLang->title = $codeData['NAME_LATN'];
                $nutsRegionLang->save();

                if (!$isNew) {
                    continue;
                }
                $wol = new WebsiteObjectLanguage();
                $wol->website_object_id = $wo->id;
                $wol->wo_language_id = $language;
                $wol->save();
            }

            Log::info("Imported NUTS code: " . $nutsRegion->code);
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Unable to import NUTS code: " . $e->getMessage());
        }
    }

    /**
     * Import cities from GeoJSON
     *
     * @param array $citiesData
     * @param string $cityType
     * @return void
     */
    public function importCities(array $citiesData, String $cityType = 'city')
    {
        $languages = LaravelLocalization::getSupportedLanguagesKeys();

        DB::beginTransaction();
        try {
            foreach ($citiesData as $cityData) {
                // Check if city state already exists and creates it if not
                $address = $cityData['address'];
                $state = State::where('code', $address['ISO3166-2-lvl4'])->first();

                if (!$state) {
                    $state = $this->_createState($address);

                    if (!$state) {
                        continue;
                    }
                }

                if (empty($cityData['type']) || $cityData['type'] != $cityType) {
                    continue;
                }

                if (empty($cityData['name'])) {
                    Log::error("City name is empty: " . json_encode($cityData));
                    continue;
                }

                $city = new City();
                $city->state_id = $state->id;
                $city->type = $cityType;
                $city->location = new Point($cityData['location'][1], $cityData['location'][0]);
                $city->location_zoom = 13;
                $city->save();

                $wo = new WebsiteObject();
                $wo->object_id = $city->id;
                $wo->object_type = City::class;
                $wo->content_type = 'city';
                $wo->save();

                foreach ($languages as $lang) {
                    $cityLanguage = new CityLanguage();
                    $cityLanguage->city_id = $city->id;
                    $cityLanguage->language_id = $lang;

                    if (!empty($cityData['other_names']["name:$lang"])) {
                        $name = $cityData['other_names']["name:$lang"];
                    } else {
                        $name = $cityData['name'];
                    }

                    $cityLanguage->title = $name;
                    $cityLanguage->save();

                    $wol = new WebsiteObjectLanguage();
                    $wol->website_object_id = $wo->id;
                    $wol->wo_language_id = $lang;
                    $wol->save();
                }
            }

            DB::commit();
        } catch (Throwable $e) {
            DB::rollBack();
            throw($e);
        }
    }

    /**
     * Create state from geo json address
     *
     * @param array $address
     * @return State|null
     */
    protected function _createState(array $address)
    {
        $state = new State();
        $state->code = $address['ISO3166-2-lvl4'];

        $country = Country::where('iso2', strtoupper($address['country_code']))->first();
        if (!$country) {
            Log::error("Country not found: " . $address['country_code']);
            return null;
        }
        $state->country_id = $country->id;

        $state->save();

        $wo = new WebsiteObject();
        $wo->object_id = $state->id;
        $wo->object_type = State::class;
        $wo->content_type = 'state';
        $wo->save();

        $languages = LaravelLocalization::getSupportedLanguagesKeys();
        foreach ($languages as $lang) {
            $stateLanguage = new StateLanguage();
            $stateLanguage->state_id = $state->id;
            $stateLanguage->language_id = $lang;
            $stateLanguage->title = !empty($address['state']) ? $address['state'] : $address['city'];
            $stateLanguage->save();

            $wol = new WebsiteObjectLanguage();
            $wol->website_object_id = $wo->id;
            $wol->wo_language_id = $lang;
            $wol->save();
        }

        return $state;
    }
}
