<?php

namespace App\Models;

use App\Helpers\GeoHelper;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;

class Location extends Model
{
    use HasFactory;

    protected $fillable = [
        'company_id',
        'name',
        'code',
        'address',
        'city',
        'province',
        'latitude',
        'longitude',
        'radius',
        'category',
        'type',
        'allow_daily_checkin',
        'allow_business_trip',
        'is_active',
        'is_primary',
        'sort_order',
    ];

    protected $casts = [
        'latitude' => 'decimal:8',
        'longitude' => 'decimal:8',
        'radius' => 'integer',
        'allow_daily_checkin' => 'boolean',
        'allow_business_trip' => 'boolean',
        'is_active' => 'boolean',
        'is_primary' => 'boolean',
        'sort_order' => 'integer',
    ];

    // ==================== RELATIONSHIPS ====================

    public function company(): BelongsTo
    {
        return $this->belongsTo(Company::class);
    }

    public function attendances(): HasMany
    {
        return $this->hasMany(Attendance::class);
    }

    // ==================== SCOPES ====================

    /**
     * Scope untuk lokasi aktif
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * Scope untuk lokasi perusahaan tertentu
     */
    public function scopeForCompany($query, int $companyId)
    {
        return $query->where('company_id', $companyId);
    }

    /**
     * Scope untuk lokasi internal (kantor milik perusahaan)
     */
    public function scopeInternal($query)
    {
        return $query->where('category', 'internal');
    }

    /**
     * Scope untuk lokasi eksternal (instansi luar)
     */
    public function scopeExternal($query)
    {
        return $query->where('category', 'external');
    }

    /**
     * Scope untuk lokasi yang bisa dipakai presensi harian
     * (Backward compatibility dengan Office)
     */
    public function scopeOffices($query)
    {
        return $query->where('allow_daily_checkin', true);
    }

    /**
     * Scope untuk lokasi yang bisa jadi tujuan dinas
     * (Backward compatibility dengan BusinessTripLocation)
     */
    public function scopeBusinessTripDestinations($query)
    {
        return $query->where('allow_business_trip', true);
    }

    /**
     * Scope untuk filter berdasarkan type
     */
    public function scopeOfType($query, string $type)
    {
        return $query->where('type', $type);
    }

    /**
     * Scope untuk filter berdasarkan multiple types
     */
    public function scopeOfTypes($query, array $types)
    {
        return $query->whereIn('type', $types);
    }

    /**
     * Scope untuk sorting: primary first, then by sort_order, then by name
     */
    public function scopeOrdered($query)
    {
        return $query->orderBy('is_primary', 'desc')
                     ->orderBy('sort_order', 'asc')
                     ->orderBy('name', 'asc');
    }

    // ==================== HELPER METHODS ====================

    /**
     * Hitung jarak dari koordinat tertentu (dalam meter)
     */
    public function distanceFrom(float $latitude, float $longitude): float
    {
        if (!$this->latitude || !$this->longitude) {
            return PHP_FLOAT_MAX;
        }

        return GeoHelper::calculateDistance(
            $latitude,
            $longitude,
            (float) $this->latitude,
            (float) $this->longitude
        );
    }

    /**
     * Cek apakah koordinat berada dalam radius lokasi
     */
    public function isWithinRadius(float $latitude, float $longitude): bool
    {
        $distance = $this->distanceFrom($latitude, $longitude);
        return $distance <= $this->radius;
    }

    /**
     * Dapatkan label type yang readable
     */
    public function getTypeLabel(): string
    {
        return match ($this->type) {
            'headquarters' => 'Kantor Pusat',
            'branch' => 'Kantor Cabang',
            'pemda' => 'Pemda',
            'kementerian' => 'Kementerian',
            'lembaga' => 'Lembaga',
            'other' => 'Lainnya',
            default => $this->type,
        };
    }

    /**
     * Dapatkan label category yang readable
     */
    public function getCategoryLabel(): string
    {
        return match ($this->category) {
            'internal' => 'Internal',
            'external' => 'Eksternal',
            default => $this->category,
        };
    }

    /**
     * Check apakah lokasi ini adalah kantor internal
     */
    public function isInternal(): bool
    {
        return $this->category === 'internal';
    }

    /**
     * Check apakah lokasi ini adalah lokasi eksternal
     */
    public function isExternal(): bool
    {
        return $this->category === 'external';
    }

    // ==================== STATIC METHODS ====================

    /**
     * Cari lokasi terdekat dari koordinat
     */
    public static function findNearest(
        int $companyId,
        float $latitude,
        float $longitude,
        ?bool $allowDailyCheckin = null,
        ?bool $allowBusinessTrip = null
    ): ?self {
        $query = static::active()->forCompany($companyId);

        if ($allowDailyCheckin !== null) {
            $query->where('allow_daily_checkin', $allowDailyCheckin);
        }

        if ($allowBusinessTrip !== null) {
            $query->where('allow_business_trip', $allowBusinessTrip);
        }

        $locations = $query->get();

        $nearest = null;
        $minDistance = PHP_FLOAT_MAX;

        foreach ($locations as $location) {
            $distance = $location->distanceFrom($latitude, $longitude);
            if ($distance < $minDistance) {
                $minDistance = $distance;
                $nearest = $location;
            }
        }

        return $nearest;
    }

    /**
     * Dapatkan lokasi terdekat dengan jarak (sorted by distance)
     */
    public static function getNearbyLocations(
        int $companyId,
        float $latitude,
        float $longitude,
        int $limit = 5,
        ?bool $allowDailyCheckin = null,
        ?bool $allowBusinessTrip = null
    ): Collection {
        $query = static::active()->forCompany($companyId);

        if ($allowDailyCheckin !== null) {
            $query->where('allow_daily_checkin', $allowDailyCheckin);
        }

        if ($allowBusinessTrip !== null) {
            $query->where('allow_business_trip', $allowBusinessTrip);
        }

        $locations = $query->get();

        // Calculate distance for each location
        $locationsWithDistance = $locations->map(function ($location) use ($latitude, $longitude) {
            $location->distance = $location->distanceFrom($latitude, $longitude);
            return $location;
        });

        // Sort by distance and take limit
        return $locationsWithDistance
            ->sortBy('distance')
            ->take($limit)
            ->values();
    }

    // ==================== MODEL EVENTS ====================

    protected static function booted(): void
    {
        // Auto-set radius berdasarkan category jika tidak diset
        static::creating(function (Location $location) {
            if (!$location->radius) {
                $location->radius = $location->category === 'internal' ? 100 : 500;
            }

            // Auto-set allow_daily_checkin untuk internal locations
            if ($location->category === 'internal' && !isset($location->allow_daily_checkin)) {
                $location->allow_daily_checkin = true;
            }
        });
    }
}
