<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class SuratTugas extends Model
{
    use HasFactory;

    protected $table = 'surat_tugas';

    protected $fillable = [
        'company_id',
        'no_st',
        'tanggal_mulai',
        'tanggal_selesai',
        'location_id',
        'custom_location_name',
        'custom_latitude',
        'custom_longitude',
        'custom_address',
        'keterangan',
        'is_active',
        'created_by',
    ];

    protected function casts(): array
    {
        return [
            'tanggal_mulai' => 'date',
            'tanggal_selesai' => 'date',
            'custom_latitude' => 'decimal:8',
            'custom_longitude' => 'decimal:8',
            'is_active' => 'boolean',
        ];
    }

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

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

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

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'surat_tugas_user')
            ->withTimestamps();
    }

    public function createdBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

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

    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    public function scopeForCompany($query, int $companyId)
    {
        return $query->where('company_id', $companyId);
    }

    public function scopeActiveOnDate($query, $date)
    {
        return $query->where('tanggal_mulai', '<=', $date)
                     ->where('tanggal_selesai', '>=', $date);
    }

    public function scopeForUser($query, int $userId)
    {
        return $query->whereHas('users', function ($q) use ($userId) {
            $q->where('users.id', $userId);
        });
    }

    // ==================== ACCESSORS ====================

    /**
     * Get location name (from location or custom)
     */
    public function getLocationNameAttribute(): ?string
    {
        if ($this->location_id && $this->relationLoaded('location') && $this->location) {
            return $this->location->name;
        }
        if ($this->location_id) {
            $this->load('location');
            return $this->location?->name;
        }
        return $this->custom_location_name;
    }

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

    /**
     * Get location coordinates (from location or custom)
     */
    public function getLocationCoordinates(): ?array
    {
        if ($this->location_id) {
            if (!$this->relationLoaded('location')) {
                $this->load('location');
            }
            if ($this->location) {
                return [
                    'latitude' => (float) $this->location->latitude,
                    'longitude' => (float) $this->location->longitude,
                    'address' => $this->location->address,
                    'name' => $this->location->name,
                ];
            }
        }

        if ($this->custom_latitude && $this->custom_longitude) {
            return [
                'latitude' => (float) $this->custom_latitude,
                'longitude' => (float) $this->custom_longitude,
                'address' => $this->custom_address,
                'name' => $this->custom_location_name,
            ];
        }

        return null;
    }

    /**
     * Check if ST is active on a specific date
     */
    public function isActiveOnDate($date): bool
    {
        $checkDate = is_string($date) ? \Carbon\Carbon::parse($date) : $date;

        return $this->is_active
            && $checkDate->gte($this->tanggal_mulai)
            && $checkDate->lte($this->tanggal_selesai);
    }

    /**
     * Check if user is assigned to this ST
     */
    public function isAssignedTo(int $userId): bool
    {
        return $this->users()->where('users.id', $userId)->exists();
    }

    /**
     * Get duration in days
     */
    public function getDurationDays(): int
    {
        return $this->tanggal_mulai->diffInDays($this->tanggal_selesai) + 1;
    }

    // ==================== STATIC HELPER ====================

    /**
     * Find active ST for a user on a specific date
     * Priority:
     * 1. ST yang memiliki user yang di-assign
     * 2. ST tanpa user (untuk semua karyawan)
     * Used by fill_location action to detect ST
     */
    public static function findActiveForUserOnDate(int $userId, $date, ?int $companyId = null): ?self
    {
        $allST = static::findAllActiveForUserOnDate($userId, $date, $companyId);
        return $allST->first();
    }

    /**
     * Find ALL active ST for a user on a specific date
     * Returns collection of ST sorted by priority:
     * 1. ST yang memiliki user yang di-assign
     * 2. ST tanpa user (untuk semua karyawan)
     */
    public static function findAllActiveForUserOnDate(int $userId, $date, ?int $companyId = null): \Illuminate\Database\Eloquent\Collection
    {
        // Get user's company_id if not provided
        if (!$companyId) {
            $user = \App\Models\User::find($userId);
            $companyId = $user?->company_id;
        }

        // Get all ST that has this user assigned
        $stWithUser = static::active()
            ->activeOnDate($date)
            ->forUser($userId)
            ->when($companyId, fn($q) => $q->where('company_id', $companyId))
            ->with('location')
            ->get();

        // Get all ST without users (applies to all employees)
        $stWithoutUser = static::active()
            ->activeOnDate($date)
            ->when($companyId, fn($q) => $q->where('company_id', $companyId))
            ->whereDoesntHave('users')
            ->with('location')
            ->get();

        // Merge and return - user-assigned ST first
        return $stWithUser->merge($stWithoutUser);
    }

    /**
     * Find all active ST on a specific date (regardless of user assignment)
     * Used for showing recommendations
     */
    public static function findAllActiveOnDate($date, ?int $companyId = null): \Illuminate\Database\Eloquent\Collection
    {
        return static::active()
            ->activeOnDate($date)
            ->when($companyId, fn($q) => $q->where('company_id', $companyId))
            ->with(['location', 'users'])
            ->get();
    }

    /**
     * Get date range that overlaps with attendance dates
     * Returns array of dates that are within ST period AND have business_trip attendance
     */
    public function getOverlappingAttendanceDates(int $userId): array
    {
        $attendances = \App\Models\Attendance::where('user_id', $userId)
            ->where('type', 'business_trip')
            ->whereBetween('date', [$this->tanggal_mulai, $this->tanggal_selesai])
            ->where(function ($q) {
                $q->whereNull('check_in_latitude')
                  ->orWhereNull('check_in_longitude');
            })
            ->orderBy('date')
            ->pluck('date')
            ->map(fn($d) => $d->format('Y-m-d'))
            ->toArray();

        return $attendances;
    }

    /**
     * Apply random radius offset to coordinates (5-30 meters)
     * Using circular distribution for more natural spread
     * ~0.00001 degrees ≈ 1.11 meters at equator
     * At Jakarta latitude (~-6°), 1 degree longitude ≈ 111km * cos(6°) ≈ 110km
     */
    public static function applyRandomOffset(float $latitude, float $longitude): array
    {
        // Random radius between 5-30 meters
        $radiusMeters = rand(5, 30);

        // Random angle in radians (0 to 2π)
        $angle = mt_rand(0, 360) * (M_PI / 180);

        // Convert meters to degrees
        // 1 degree latitude ≈ 111,320 meters
        // 1 degree longitude ≈ 111,320 * cos(latitude) meters
        $metersPerDegreeLat = 111320;
        $metersPerDegreeLon = 111320 * cos(deg2rad($latitude));

        // Calculate offset in degrees
        $latOffset = ($radiusMeters * sin($angle)) / $metersPerDegreeLat;
        $lonOffset = ($radiusMeters * cos($angle)) / $metersPerDegreeLon;

        return [
            'latitude' => number_format($latitude + $latOffset, 6, '.', ''),
            'longitude' => number_format($longitude + $lonOffset, 6, '.', ''),
        ];
    }

    /**
     * Apply random radius offset to coordinates for dinas (business trip) (5-50 meters)
     * Using circular distribution for more natural spread
     * More variation for business trips to simulate different locations within the area
     */
    public static function applyRandomOffsetDinas(float $latitude, float $longitude): array
    {
        // Random radius between 5-50 meters for dinas
        $radiusMeters = rand(5, 50);

        // Random angle in radians (0 to 2π)
        $angle = mt_rand(0, 360) * (M_PI / 180);

        // Convert meters to degrees
        // 1 degree latitude ≈ 111,320 meters
        // 1 degree longitude ≈ 111,320 * cos(latitude) meters
        $metersPerDegreeLat = 111320;
        $metersPerDegreeLon = 111320 * cos(deg2rad($latitude));

        // Calculate offset in degrees
        $latOffset = ($radiusMeters * sin($angle)) / $metersPerDegreeLat;
        $lonOffset = ($radiusMeters * cos($angle)) / $metersPerDegreeLon;

        return [
            'latitude' => number_format($latitude + $latOffset, 6, '.', ''),
            'longitude' => number_format($longitude + $lonOffset, 6, '.', ''),
        ];
    }

    /**
     * Apply random radius offset to coordinates for kantor (office) (5-30 meters)
     * Using circular distribution for more natural spread
     * Less variation for office locations
     */
    public static function applyRandomOffsetKantor(float $latitude, float $longitude): array
    {
        // Random radius between 5-30 meters for kantor
        $radiusMeters = rand(5, 30);

        // Random angle in radians (0 to 2π)
        $angle = mt_rand(0, 360) * (M_PI / 180);

        // Convert meters to degrees
        // 1 degree latitude ≈ 111,320 meters
        // 1 degree longitude ≈ 111,320 * cos(latitude) meters
        $metersPerDegreeLat = 111320;
        $metersPerDegreeLon = 111320 * cos(deg2rad($latitude));

        // Calculate offset in degrees
        $latOffset = ($radiusMeters * sin($angle)) / $metersPerDegreeLat;
        $lonOffset = ($radiusMeters * cos($angle)) / $metersPerDegreeLon;

        return [
            'latitude' => number_format($latitude + $latOffset, 6, '.', ''),
            'longitude' => number_format($longitude + $lonOffset, 6, '.', ''),
        ];
    }
}
