<?php

namespace App\Models\Locations;

use App\Models\Locations\City;
use App\Models\Locations\Country;
use App\Models\Locations\District;
use App\Models\Locations\Division;
use App\Models\Meeting;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Location extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'country_id', 'division_id', 'city_id', 'district_id', 'locatable_id', 'locatable_type'];

    /**
    * Return related country.
    * one-to-many
    * @return void
    */
    public function country()
    {
        return $this->belongsTo(Country::class);
    }

    /**
     * Get the most appropriate country for this location.
     * Tries multiple sources in order of preference.
     * If resolved it syncs the location's missing country_id.
     *
     * @return Country|null
     */
    public function getCountry()
    {
        // 1. Direct country relationship
        if ($this->country_id && $this->country) {
            return $this->country;
        }

        // 2. Country from division
        if ($this->division_id && $this->division && $this->division->country_id) {
            $this->syncCountryFromDivision();
            return $this->fresh()->country;
        }

        // 3. Country from city
        if ($this->city_id && $this->city && $this->city->country_id) {
            $this->syncCountryFromCity();
            return $this->fresh()->country;
        }

        // 4. Country from district's city (if district exists)
        if ($this->district_id && $this->district && $this->district->city_id) {
            // First ensure we have city synced
            if (!$this->city_id) {
                $this->syncCityFromDistrict();
            }

            // Now try to get country from city
            if ($this->city && $this->city->country_id) {
                $this->syncCountryFromCity();
                return $this->fresh()->country;
            }
        }

        return null;
    }


    /**
     * Update the location's country_id based on the division's country.
     *
     * @return bool
     */
    public function syncCountryFromDivision()
    {
        if ($this->division && $this->division->country_id) {
            $this->country_id = $this->division->country_id;
            return $this->save();
        }

        return false;
    }

    /**
     * Update the location's country_id based on the city's country.
     *
     * @return bool
     */
    public function syncCountryFromCity()
    {
        if ($this->city && $this->city->country_id) {
            $this->country_id = $this->city->country_id;
            return $this->save();
        }

        return false;
    }


    /**
    * Return all related divisions.
    * One-to-many
    * @return void
    */
    public function division()
    {
        return $this->belongsTo(Division::class);
    }

    /**
     * Get the most appropriate division for this location.
     * Tries multiple sources in order of preference.
     * If resolved it syncs the location's missing division_id.
     *
     * @return Division|null
     */
    public function getDivision()
    {
        // 1. Direct division relationship
        if ($this->division_id && $this->division) {
            return $this->division;
        }

        // 2. Division from city
        if ($this->city_id && $this->city && $this->city->division_id) {
            $this->syncDivisionFromCity();
            return $this->fresh()->division; // Fresh instance to get updated division_id
        }

        // 3. Division from district's city (if district exists)
        if ($this->district_id && $this->district && $this->district->city_id) {
            // First sync city from district if city_id is missing
            if (!$this->city_id) {
                $this->syncCityFromDistrict();
            }

            // Now try to get division from city
            if ($this->city && $this->city->division_id) {
                $this->syncDivisionFromCity();
                return $this->fresh()->division;
            }
        }

        return null;
    }


    /**
     * Update the location's country_id based on the division's country.
     *
     * @return bool
     */
    public function syncCountryFromDistrict()
    {
        if ($this->division && $this->division->country_id) {
            $this->country_id = $this->division->country_id;
            return $this->save();
        }

        return false;
    }

    /**
     * Update the location's division_id based on the city's division.
     *
     * @return bool
     */
    public function syncDivisionFromCity()
    {
        if ($this->city && $this->city->division_id) {
            $this->division_id = $this->city->division_id;
            return $this->save();
        }

        return false;
    }

    /**
     * Update the location's division_id based on the city's division.
     *
     * @return bool
     */
    public function syncCityFromDistrict()
    {
        if ($this->district && $this->district->city_id) {
            $this->city_id = $this->district->city_id;
            return $this->save();
        }

        return false;
    }


    /**
    * Return related city.
    * One-to-many
    * @return void
    */
    public function city()
    {
        return $this->belongsTo(City::class);
    }


    /**
    * Return related district.
    * One-to-many
    * @return void
    */
    public function district()
    {
        return $this->belongsTo(District::class);
    }


    /**
     * Sync all missing location hierarchy data.
     * This method will populate all missing IDs based on available relationships.
     *
     * @return array Returns what was synced
     */
    public function syncAllLocationData()
    {
        $synced = [];

        // Step 1: Sync city from district if missing
        if (!$this->city_id && $this->district_id) {
            if ($this->syncCityFromDistrict()) {
                $synced[] = 'city_from_district';
            }
        }

        // Step 2: Sync division from city if missing
        if (!$this->division_id && $this->city_id) {
            if ($this->syncDivisionFromCity()) {
                $synced[] = 'division_from_city';
            }
        }

        // Step 3: Sync country from division if missing
        if (!$this->country_id && $this->division_id) {
            if ($this->syncCountryFromDivision()) {
                $synced[] = 'country_from_division';
            }
        }

        // Step 4: Fallback - sync country from city if still missing
        if (!$this->country_id && $this->city_id) {
            if ($this->syncCountryFromCity()) {
                $synced[] = 'country_from_city';
            }
        }

        return $synced;
    }

    /**
     * Get a complete location hierarchy.
     * This method ensures all data is synced and returns the complete hierarchy.
     *
     * @return array
     */
    public function getCompleteHierarchy()
    {
        // Sync all data first
        $this->syncAllLocationData();

        // Refresh the model to get updated relationships
        $location = $this->fresh();

        return [
            'country' => $location->country,
            'division' => $location->getDivision(),
            'city' => $location->city,
            'district' => $location->district,
        ];
    }


    /**
    * Return related locatable (i.e. user or organization).
    * One-to-many polymorph
    * @return void
    */
    public function locatable()
    {
        return $this->morphTo();
    }


    /**
     * Return related meeting.
     * one-to-many
     * @return void
     */
    public function meeting()
    {
        return $this->hasOne(Meeting::class);
    }

}
