<?php

namespace App\Models;

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

/**
 * SmsLog Model
 * 
 * Stores all SMS API interactions for debugging, auditing, and analytics.
 * Each log entry captures the full request/response cycle for SMS messages
 * sent through Africa's Talking API.
 * 
 * Features:
 * - Tracks message status throughout the delivery lifecycle
 * - Stores full API response for debugging
 * - Records execution time for performance monitoring
 * - Links to user and configuration for auditing
 * 
 * @property int $id
 * @property string $recipient Phone number of the SMS recipient
 * @property string $message Content of the SMS message
 * @property int $message_length Character count of the message
 * @property int $segment_count Number of SMS segments used
 * @property string $status Current status (pending/sent/failed/delivered/undelivered)
 * @property string|null $message_id Unique message ID from Africa's Talking API
 * @property array|null $response_body Full JSON response from API
 * @property string|null $error_message Error message if SMS failed
 * @property int $execution_time_ms Time taken to send SMS in milliseconds
 * @property float|null $cost Cost of the SMS
 * @property int|null $sms_configuration_id Reference to the SMS configuration used
 * @property int|null $user_id User who initiated the SMS
 * @property \Illuminate\Support\Carbon $created_at When the SMS log was created
 * 
 * @property-read \App\Models\SmsConfiguration|null $configuration
 * @property-read \App\Models\User|null $user
 */
class SmsLog extends Model
{
    use HasFactory;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'sms_logs';

    /**
     * Indicates if the model should be timestamped.
     * Logs only have created_at, not updated_at.
     *
     * @var bool
     */
    public $timestamps = false;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'recipient',
        'message',
        'message_length',
        'segment_count',
        'status',
        'message_id',
        'response_body',
        'error_message',
        'execution_time_ms',
        'cost',
        'sms_configuration_id',
        'user_id',
        'created_at',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'response_body' => 'array',
        'message_length' => 'integer',
        'segment_count' => 'integer',
        'execution_time_ms' => 'integer',
        'cost' => 'decimal:4',
        'created_at' => 'datetime',
    ];

    /**
     * The attributes that should be hidden for serialization.
     * Response body may contain sensitive data.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'response_body',
    ];

    /**
     * Status constants for easy reference.
     */
    public const STATUS_PENDING = 'pending';
    public const STATUS_SENT = 'sent';
    public const STATUS_FAILED = 'failed';
    public const STATUS_DELIVERED = 'delivered';
    public const STATUS_UNDELIVERED = 'undelivered';

    /**
     * Get all possible status values.
     *
     * @return array
     */
    public static function getStatuses(): array
    {
        return [
            self::STATUS_PENDING,
            self::STATUS_SENT,
            self::STATUS_FAILED,
            self::STATUS_DELIVERED,
            self::STATUS_UNDELIVERED,
        ];
    }

    /**
     * Get the SMS configuration that was used for this log.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function configuration(): BelongsTo
    {
        return $this->belongsTo(SmsConfiguration::class, 'sms_configuration_id');
    }

    /**
     * Get the user who initiated this SMS.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(\App\Models\User::class, 'user_id');
    }

    /**
     * Scope to filter by status.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $status
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeWithStatus($query, string $status)
    {
        return $query->where('status', $status);
    }

    /**
     * Scope to get pending SMS logs.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePending($query)
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    /**
     * Scope to get sent SMS logs.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeSent($query)
    {
        return $query->where('status', self::STATUS_SENT);
    }

    /**
     * Scope to get failed SMS logs.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeFailed($query)
    {
        return $query->where('status', self::STATUS_FAILED);
    }

    /**
     * Scope to get delivered SMS logs.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeDelivered($query)
    {
        return $query->where('status', self::STATUS_DELIVERED);
    }

    /**
     * Scope to filter by recipient phone number.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $recipient
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeToRecipient($query, string $recipient)
    {
        return $query->where('recipient', $recipient);
    }

    /**
     * Scope to filter by date range.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param \Illuminate\Support\Carbon $from
     * @param \Illuminate\Support\Carbon $to
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeDateRange($query, $from, $to)
    {
        return $query->whereBetween('created_at', [$from, $to]);
    }

    /**
     * Scope to get logs from today.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeToday($query)
    {
        return $query->whereDate('created_at', today());
    }

    /**
     * Scope to get logs from this month.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeThisMonth($query)
    {
        return $query->whereMonth('created_at', now()->month)
                     ->whereYear('created_at', now()->year);
    }

    /**
     * Scope to search by recipient or message content.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $search
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeSearch($query, string $search)
    {
        return $query->where(function ($q) use ($search) {
            $q->where('recipient', 'like', "%{$search}%")
              ->orWhere('message', 'like', "%{$search}%")
              ->orWhere('message_id', 'like', "%{$search}%");
        });
    }

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

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

    /**
     * Check if the SMS is pending.
     *
     * @return bool
     */
    public function isPending(): bool
    {
        return $this->status === self::STATUS_PENDING;
    }

    /**
     * Check if the SMS was delivered.
     *
     * @return bool
     */
    public function isDelivered(): bool
    {
        return $this->status === self::STATUS_DELIVERED;
    }

    /**
     * Mark the SMS as sent.
     *
     * @param string $messageId
     * @param array $response
     * @param float|null $cost
     * @return void
     */
    public function markAsSent(string $messageId, array $response, ?float $cost = null): void
    {
        $this->update([
            'status' => self::STATUS_SENT,
            'message_id' => $messageId,
            'response_body' => $response,
            'cost' => $cost,
        ]);
    }

    /**
     * Mark the SMS as failed.
     *
     * @param string $errorMessage
     * @param array|null $response
     * @return void
     */
    public function markAsFailed(string $errorMessage, ?array $response = null): void
    {
        $this->update([
            'status' => self::STATUS_FAILED,
            'error_message' => $errorMessage,
            'response_body' => $response,
        ]);
    }

    /**
     * Get the status badge color for display.
     *
     * @return string
     */
    public function getStatusColor(): string
    {
        return match ($this->status) {
            self::STATUS_PENDING => 'yellow',
            self::STATUS_SENT => 'blue',
            self::STATUS_DELIVERED => 'green',
            self::STATUS_FAILED, self::STATUS_UNDELIVERED => 'red',
            default => 'gray',
        };
    }

    /**
     * Get formatted status for display.
     *
     * @return string
     */
    public function getFormattedStatus(): string
    {
        return ucfirst($this->status);
    }

    /**
     * Get the execution time in seconds.
     *
     * @return float
     */
    public function getExecutionTimeSeconds(): float
    {
        return $this->execution_time_ms / 1000;
    }

    /**
     * Calculate message segments based on length.
     * Standard SMS is 160 characters, or 153 for concatenated messages.
     *
     * @param string $message
     * @return int
     */
    public static function calculateSegments(string $message): int
    {
        $length = strlen($message);
        
        if ($length <= 160) {
            return 1;
        }
        
        // For messages over 160 chars, each segment can hold 153 chars
        return (int) ceil($length / 153);
    }
}
