<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Builder;

/**
 * ChatAgent Model
 * 
 * Represents a user designated as a chat agent.
 * Agents can be from any role and are appointed by Super Manager or Manager.
 * 
 * Requirements: 4.1
 * 
 * @property int $id
 * @property int $user_id
 * @property int|null $appointed_by
 * @property array|null $categories
 * @property string $status
 * @property bool $is_active
 * @property int $max_concurrent_chats
 * @property int $current_chats
 * @property int $total_conversations
 * @property float $avg_response_time
 * @property float $satisfaction_rating
 * @property array|null $availability_schedule
 * @property \Carbon\Carbon|null $last_active_at
 */
class ChatAgent extends Model
{
    use SoftDeletes;

    /**
     * Status constants
     */
    const STATUS_ONLINE = 'online';
    const STATUS_AWAY = 'away';
    const STATUS_OFFLINE = 'offline';
    const STATUS_BUSY = 'busy';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'user_id',
        'appointed_by',
        'categories',
        'status',
        'is_active',
        'max_concurrent_chats',
        'current_chats',
        'total_conversations',
        'avg_response_time',
        'satisfaction_rating',
        'availability_schedule',
        'last_active_at',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'categories' => 'array',
        'is_active' => 'boolean',
        'max_concurrent_chats' => 'integer',
        'current_chats' => 'integer',
        'total_conversations' => 'integer',
        'avg_response_time' => 'decimal:2',
        'satisfaction_rating' => 'decimal:2',
        'availability_schedule' => 'array',
        'last_active_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
        'deleted_at' => 'datetime',
    ];

    /**
     * Status colors for UI.
     */
    public static function statusColors(): array
    {
        return [
            self::STATUS_ONLINE => 'bg-green-500',
            self::STATUS_AWAY => 'bg-yellow-500',
            self::STATUS_OFFLINE => 'bg-gray-400',
            self::STATUS_BUSY => 'bg-red-500',
        ];
    }

    /**
     * Status labels for UI.
     */
    public static function statusLabels(): array
    {
        return [
            self::STATUS_ONLINE => 'Online',
            self::STATUS_AWAY => 'Away',
            self::STATUS_OFFLINE => 'Offline',
            self::STATUS_BUSY => 'Busy',
        ];
    }

    /**
     * Get the user who is the agent.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    /**
     * Get the user who appointed this agent.
     */
    public function appointedByUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'appointed_by');
    }

    /**
     * Alias for appointedByUser (used by ChatService).
     */
    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'appointed_by');
    }

    /**
     * Get conversations assigned to this agent.
     */
    public function conversations(): HasMany
    {
        return $this->hasMany(ChatConversation::class, 'agent_id');
    }

    /**
     * Get active conversations for this agent.
     */
    public function activeConversations(): HasMany
    {
        return $this->conversations()->whereIn('status', [
            ChatConversation::STATUS_WAITING,
            ChatConversation::STATUS_ACTIVE,
        ]);
    }

    /**
     * Scope for active agents.
     */
    public function scopeActive(Builder $query): Builder
    {
        return $query->where('is_active', true);
    }

    /**
     * Scope for inactive agents.
     */
    public function scopeInactive(Builder $query): Builder
    {
        return $query->where('is_active', false);
    }

    /**
     * Scope for online agents.
     */
    public function scopeOnline(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_ONLINE)
                     ->where('is_active', true);
    }

    /**
     * Scope for available agents (online and not at capacity).
     */
    public function scopeAvailable(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_ONLINE)
                     ->where('is_active', true)
                     ->whereColumn('current_chats', '<', 'max_concurrent_chats');
    }

    /**
     * Scope for agents in a category.
     */
    public function scopeInCategory(Builder $query, int $categoryId): Builder
    {
        return $query->where(function ($q) use ($categoryId) {
            $q->whereNull('categories')
              ->orWhereJsonContains('categories', $categoryId);
        });
    }

    /**
     * Scope for searching agents.
     */
    public function scopeSearch(Builder $query, string $search): Builder
    {
        return $query->whereHas('user', function ($q) use ($search) {
            $q->where('name', 'like', "%{$search}%")
              ->orWhere('email', 'like', "%{$search}%");
        });
    }

    /**
     * Check if agent is online.
     */
    public function isOnline(): bool
    {
        return $this->status === self::STATUS_ONLINE && $this->is_active;
    }

    /**
     * Check if agent is available for new chats.
     */
    public function isAvailable(): bool
    {
        return $this->isOnline() && $this->current_chats < $this->max_concurrent_chats;
    }

    /**
     * Check if agent can handle more chats.
     */
    public function canAcceptChat(): bool
    {
        return $this->is_active && $this->current_chats < $this->max_concurrent_chats;
    }

    /**
     * Check if agent handles a specific category.
     */
    public function handlesCategory(int $categoryId): bool
    {
        if (empty($this->categories)) {
            return true; // Handles all categories
        }
        
        return in_array($categoryId, $this->categories);
    }

    /**
     * Set agent status to online.
     */
    public function setOnline(): void
    {
        $this->update([
            'status' => self::STATUS_ONLINE,
            'last_active_at' => now(),
        ]);
    }

    /**
     * Set agent status to away.
     */
    public function setAway(): void
    {
        $this->update(['status' => self::STATUS_AWAY]);
    }

    /**
     * Set agent status to offline.
     */
    public function setOffline(): void
    {
        $this->update(['status' => self::STATUS_OFFLINE]);
    }

    /**
     * Set agent status to busy.
     */
    public function setBusy(): void
    {
        $this->update(['status' => self::STATUS_BUSY]);
    }

    /**
     * Activate the agent.
     */
    public function activate(): void
    {
        $this->update(['is_active' => true]);
    }

    /**
     * Deactivate the agent.
     */
    public function deactivate(): void
    {
        $this->update([
            'is_active' => false,
            'status' => self::STATUS_OFFLINE,
        ]);
    }

    /**
     * Increment current chat count.
     */
    public function incrementChatCount(): void
    {
        $this->increment('current_chats');
        $this->increment('total_conversations');
        $this->update(['last_active_at' => now()]);
    }

    /**
     * Decrement current chat count.
     */
    public function decrementChatCount(): void
    {
        if ($this->current_chats > 0) {
            $this->decrement('current_chats');
        }
    }

    /**
     * Update average response time.
     */
    public function updateAverageResponseTime(int $responseTime): void
    {
        $total = $this->total_conversations;
        $current = $this->avg_response_time;
        
        $newAverage = (($current * ($total - 1)) + $responseTime) / $total;
        
        $this->update(['avg_response_time' => round($newAverage, 2)]);
    }

    /**
     * Update satisfaction rating.
     */
    public function updateSatisfactionRating(float $rating): void
    {
        $total = $this->total_conversations;
        $current = $this->satisfaction_rating;
        
        $newAverage = (($current * ($total - 1)) + $rating) / $total;
        
        $this->update(['satisfaction_rating' => round($newAverage, 2)]);
    }

    /**
     * Get the status badge HTML.
     */
    public function getStatusBadgeAttribute(): string
    {
        $color = self::statusColors()[$this->status] ?? 'bg-gray-400';
        $label = self::statusLabels()[$this->status] ?? 'Unknown';
        
        return '<span class="inline-flex items-center gap-2">' .
               '<span class="w-2 h-2 rounded-full ' . $color . '"></span>' .
               $label . '</span>';
    }

    /**
     * Get workload percentage.
     */
    public function getWorkloadPercentageAttribute(): int
    {
        if ($this->max_concurrent_chats <= 0) {
            return 0;
        }
        
        return min(100, (int) round(($this->current_chats / $this->max_concurrent_chats) * 100));
    }

    /**
     * Get formatted average response time.
     */
    public function getFormattedResponseTimeAttribute(): string
    {
        $minutes = $this->avg_response_time;
        
        if ($minutes < 1) {
            return round($minutes * 60) . ' seconds';
        }
        
        if ($minutes < 60) {
            return round($minutes) . ' minutes';
        }
        
        $hours = floor($minutes / 60);
        $remainingMinutes = round($minutes % 60);
        
        return $hours . 'h ' . $remainingMinutes . 'm';
    }

    /**
     * Get availability for display.
     */
    public function getAvailabilityTextAttribute(): string
    {
        if (!$this->is_active) {
            return 'Inactive';
        }
        
        return $this->current_chats . ' / ' . $this->max_concurrent_chats . ' active chats';
    }

    /**
     * Find best available agent for a category.
     */
    public static function findBestAvailable(?int $categoryId = null): ?self
    {
        $query = self::query()
            ->where('status', self::STATUS_ONLINE)
            ->where('is_active', true)
            ->whereColumn('current_chats', '<', 'max_concurrent_chats');
        
        if ($categoryId) {
            $query->where(function ($q) use ($categoryId) {
                $q->whereNull('categories')
                  ->orWhereJsonContains('categories', $categoryId);
            });
        }
        
        // Order by least current chats, then by lowest avg response time
        return $query->orderBy('current_chats')
                     ->orderBy('avg_response_time')
                     ->first();
    }

    /**
     * Get online agents count.
     */
    public static function getOnlineCount(): int
    {
        return self::where('status', self::STATUS_ONLINE)
            ->where('is_active', true)
            ->count();
    }

    /**
     * Get available agents count.
     */
    public static function getAvailableCount(): int
    {
        return self::where('status', self::STATUS_ONLINE)
            ->where('is_active', true)
            ->whereColumn('current_chats', '<', 'max_concurrent_chats')
            ->count();
    }

    /**
     * Get total active conversations across all agents.
     */
    public static function getTotalActiveChats(): int
    {
        return self::sum('current_chats');
    }
}
