<?php

namespace App\Models;

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

/**
 * SmsMessage Model
 * 
 * Represents an SMS message record with tracking.
 * Integrates with Africa's Talking API.
 * 
 * Requirements: 2.1, 2.4, 2.8, 2.10, 8.2
 * 
 * @property int $id
 * @property int $sender_id
 * @property array $recipients
 * @property string $message
 * @property string|null $sender_id_name
 * @property int|null $template_id
 * @property \Carbon\Carbon|null $scheduled_at
 * @property \Carbon\Carbon|null $sent_at
 * @property string $status
 * @property string|null $message_id
 * @property float $cost
 * @property array|null $delivery_reports
 * @property string|null $error_message
 * @property string $recipient_type
 * @property string|null $target_role
 */
class SmsMessage extends Model
{
    use SoftDeletes;

    /**
     * Status constants
     */
    const STATUS_DRAFT = 'draft';
    const STATUS_SCHEDULED = 'scheduled';
    const STATUS_SENDING = 'sending';
    const STATUS_SENT = 'sent';
    const STATUS_FAILED = 'failed';
    const STATUS_PARTIAL = 'partial';

    /**
     * Recipient type constants
     */
    const RECIPIENT_MANUAL = 'manual';
    const RECIPIENT_CSV = 'csv';
    const RECIPIENT_ROLE_GROUP = 'role_group';
    const RECIPIENT_MIXED = 'mixed';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'sender_id',
        'recipients',
        'message',
        'sender_id_name',
        'template_id',
        'scheduled_at',
        'sent_at',
        'status',
        'message_id',
        'cost',
        'delivery_reports',
        'error_message',
        'recipient_type',
        'target_role',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'recipients' => 'array',
        'delivery_reports' => 'array',
        'scheduled_at' => 'datetime',
        'sent_at' => 'datetime',
        'cost' => 'decimal:4',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
        'deleted_at' => 'datetime',
    ];

    /**
     * Status color mapping for UI.
     */
    public static function statusColors(): array
    {
        return [
            self::STATUS_DRAFT => 'bg-gray-100 text-gray-800',
            self::STATUS_SCHEDULED => 'bg-blue-100 text-blue-800',
            self::STATUS_SENDING => 'bg-yellow-100 text-yellow-800',
            self::STATUS_SENT => 'bg-green-100 text-green-800',
            self::STATUS_FAILED => 'bg-red-100 text-red-800',
            self::STATUS_PARTIAL => 'bg-orange-100 text-orange-800',
        ];
    }

    /**
     * Get the user who sent this SMS.
     */
    public function sender(): BelongsTo
    {
        return $this->belongsTo(User::class, 'sender_id');
    }

    /**
     * Get the template used (if any).
     */
    public function template(): BelongsTo
    {
        return $this->belongsTo(SmsTemplate::class, 'template_id');
    }

    /**
     * Scope for sent messages.
     */
    public function scopeSent(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_SENT);
    }

    /**
     * Scope for scheduled messages.
     */
    public function scopeScheduled(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_SCHEDULED);
    }

    /**
     * Scope for draft messages.
     */
    public function scopeDraft(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_DRAFT);
    }

    /**
     * Scope for failed messages.
     */
    public function scopeFailed(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_FAILED);
    }

    /**
     * Scope for messages sent by a specific user.
     */
    public function scopeSentBy(Builder $query, int $userId): Builder
    {
        return $query->where('sender_id', $userId);
    }

    /**
     * Scope for searching messages.
     */
    public function scopeSearch(Builder $query, string $search): Builder
    {
        return $query->where(function ($q) use ($search) {
            $q->where('message', 'like', "%{$search}%")
              ->orWhereJsonContains('recipients', ['phone' => $search])
              ->orWhereJsonContains('recipients', $search);
        });
    }

    /**
     * Scope for messages in a date range.
     */
    public function scopeDateRange(Builder $query, $startDate, $endDate): Builder
    {
        return $query->whereBetween('created_at', [$startDate, $endDate]);
    }

    /**
     * Scope for pending scheduled messages.
     */
    public function scopePendingScheduled(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_SCHEDULED)
                     ->where('scheduled_at', '<=', now());
    }

    /**
     * Get the number of recipients.
     */
    public function getRecipientCountAttribute(): int
    {
        return count($this->recipients ?? []);
    }

    /**
     * Get recipient phone numbers only.
     */
    public function getRecipientPhones(): array
    {
        return collect($this->recipients ?? [])
            ->pluck('phone')
            ->filter()
            ->toArray();
    }

    /**
     * Get the status badge HTML.
     */
    public function getStatusBadgeAttribute(): string
    {
        $color = self::statusColors()[$this->status] ?? 'bg-gray-100 text-gray-800';
        return '<span class="px-3 py-1 rounded-full text-xs font-semibold ' . $color . '">' .
               ucfirst($this->status) . '</span>';
    }

    /**
     * Get character count of message.
     */
    public function getCharacterCountAttribute(): int
    {
        return strlen($this->message);
    }

    /**
     * Estimate number of SMS segments needed.
     */
    public function getEstimatedSegmentsAttribute(): int
    {
        $length = $this->character_count;
        
        if ($length <= 160) {
            return 1;
        }
        
        return (int) ceil($length / 153);
    }

    /**
     * Estimate total cost for the message.
     */
    public function getEstimatedCostAttribute(): float
    {
        // This would integrate with Africa's Talking pricing
        // Placeholder rate: KES 1.00 per segment
        $ratePerSegment = 1.00;
        return $this->estimated_segments * $this->recipient_count * $ratePerSegment;
    }

    /**
     * Check if message is scheduled for future.
     */
    public function isScheduled(): bool
    {
        return $this->status === self::STATUS_SCHEDULED && 
               $this->scheduled_at && 
               $this->scheduled_at->isFuture();
    }

    /**
     * Check if message was sent.
     */
    public function isSent(): bool
    {
        return $this->status === self::STATUS_SENT;
    }

    /**
     * Check if message failed.
     */
    public function isFailed(): bool
    {
        return $this->status === self::STATUS_FAILED;
    }

    /**
     * Mark as sent.
     */
    public function markAsSent(string $messageId, float $cost, array $deliveryReports = []): void
    {
        $this->update([
            'status' => self::STATUS_SENT,
            'message_id' => $messageId,
            'cost' => $cost,
            'sent_at' => now(),
            'delivery_reports' => $deliveryReports,
        ]);
    }

    /**
     * Mark as failed.
     */
    public function markAsFailed(string $errorMessage): void
    {
        $this->update([
            'status' => self::STATUS_FAILED,
            'error_message' => $errorMessage,
        ]);
    }

    /**
     * Mark as sending.
     */
    public function markAsSending(): void
    {
        $this->update(['status' => self::STATUS_SENDING]);
    }

    /**
     * Get delivery statistics.
     */
    public function getDeliveryStats(): array
    {
        $reports = $this->delivery_reports ?? [];
        
        $total = count($reports);
        $delivered = count(array_filter($reports, fn($r) => ($r['status'] ?? '') === 'Delivered'));
        $failed = count(array_filter($reports, fn($r) => ($r['status'] ?? '') === 'Failed'));
        $pending = $total - $delivered - $failed;

        return [
            'total' => $total,
            'delivered' => $delivered,
            'failed' => $failed,
            'pending' => $pending,
            'delivery_rate' => $total > 0 ? round(($delivered / $total) * 100, 2) : 0,
        ];
    }
}
