<?php

namespace App\Models\Contact\Hub;

use App\Enum\Contact\MessageChannel;
use App\Enum\Contact\MessageDirection;
use App\Enum\Contact\MessageOrigin;
use App\Enum\Contact\MessageProvider;
use App\Enum\Contact\MessageStatus;
use App\Models\Contact\ContactTemplate;
use App\Models\Solicitor\SolicitorEmailSender;
use App\Models\Solicitor\SolicitorSmsSender;
use App\Models\User\User;
use App\Trait\BelongsToSolicitor;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;

class ContactMessage extends Model
{
    use SoftDeletes, BelongsToSolicitor;

    protected $table = 'contact_messages';

    protected $fillable = [
        'solicitor_id',
        'channel', 'direction', 'provider', 'status',
        'origin_type', 'origin_detail',
        'messageable_type', 'messageable_id',
        'related_type', 'related_id',
        'template_id', 'sender_email_id', 'sms_sender_id',
        'recipient_name', 'recipient_email', 'recipient_phone',
        'sender_display', 'sender_value', 'reply_to',
        'subject', 'body_text', 'body_html', 'rendered_placeholders',
        'provider_message_id', 'provider_payload', 'failure_reason',
        'thread_key', 'in_reply_to_provider_id', 'dedupe_hash',
        'from_email',
        'classification', 'classification_status', 'classification_confidence', 'classification_notes',
        'triage_queue', 'triage_status',
        'queued_at', 'sent_at', 'delivered_at', 'opened_at', 'failed_at', 'read_at',
        'created_by', 'updated_by',
    ];

    protected $casts = [
        'channel' => MessageChannel::class,
        'direction' => MessageDirection::class,
        'provider' => MessageProvider::class,
        'status' => MessageStatus::class,
        'origin_type' => MessageOrigin::class,
        'origin_detail' => 'array',
        'rendered_placeholders' => 'array',
        'provider_payload' => 'array',
        'queued_at' => 'datetime',
        'sent_at' => 'datetime',
        'delivered_at' => 'datetime',
        'opened_at' => 'datetime',
        'failed_at' => 'datetime',
        'read_at' => 'datetime',
    ];

    public function messageable(): MorphTo
    {
        return $this->morphTo();
    }

    public function related(): MorphTo
    {
        return $this->morphTo();
    }

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

    public function senderEmail(): BelongsTo
    {
        return $this->belongsTo(SolicitorEmailSender::class, 'sender_email_id');
    }

    public function smsSender(): BelongsTo
    {
        return $this->belongsTo(SolicitorSmsSender::class, 'sms_sender_id');
    }

    public function events(): HasMany
    {
        return $this->hasMany(ContactMessageEvent::class)->orderBy('occurred_at');
    }

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

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

    public function scopeChannel(Builder $q, MessageChannel $c): Builder
    {
        return $q->where('channel', $c->value);
    }

    public function scopeOutbound(Builder $q): Builder
    {
        return $q->where('direction', MessageDirection::OUTBOUND->value);
    }

    public function scopeInbound(Builder $q): Builder
    {
        return $q->where('direction', MessageDirection::INBOUND->value);
    }

    public function scopeRecent(Builder $q): Builder
    {
        return $q->orderByDesc('created_at');
    }

    public function scopeForMessageable(Builder $q, Model $m): Builder
    {
        return $q
            ->where('messageable_type', $m->getMorphClass())
            ->where('messageable_id', $m->getKey());
    }

    /**
     * Resolve a click-through URL for the linked messageable record, or null
     * if no UI route exists for that model class.
     */
    public function messageableUrl(): ?string
    {
        $target = $this->messageable;
        if (!$target) {
            return null;
        }

        return match ($target::class) {
            \App\Models\Client\Client::class => route('clients.show', $target),
            \App\Models\Claim\Claim::class => $target->client
                ? route('clients.show', $target->client)
                : null,
            default => null,
        };
    }

    /**
     * Short human label for the messageable, e.g. "Client: Mr John Smith".
     */
    public function messageableLabel(): ?string
    {
        $target = $this->messageable;
        if (!$target) {
            return null;
        }

        $type = class_basename($this->messageable_type);

        $name = match (true) {
            $target instanceof \App\Models\Client\Client => trim(
                ($target->getRawOriginal('name_first') ?? '') . ' ' .
                ($target->getRawOriginal('name_last') ?? '')
            ),
            $target instanceof \App\Models\Claim\Claim => $target->claim_id
                ?? ($target->client ? trim(
                    ($target->client->getRawOriginal('name_first') ?? '') . ' ' .
                    ($target->client->getRawOriginal('name_last') ?? '')
                ) : null),
            default => null,
        };

        return trim($type . ($name ? ': ' . $name : ' #' . $this->messageable_id));
    }

    /**
     * Build the prompt string fed to AI agents.
     * Includes the email body plus any extracted attachment text, each labelled.
     */
    public function aiPrompt(int $bodyLimit = 3000, int $attachmentLimit = 2000): string
    {
        $parts = array_filter([
            'FROM: '    . ($this->from_email ?: 'unknown'),
            'SUBJECT: ' . ($this->subject    ?: '(no subject)'),
            '',
            mb_substr(strip_tags($this->body_text ?: $this->body_html ?: ''), 0, $bodyLimit),
        ]);

        $this->loadMissing('attachments');

        foreach ($this->attachments as $attachment) {
            if (!empty($attachment->extracted_text)) {
                $parts[] = '';
                $parts[] = "--- ATTACHMENT: {$attachment->filename} ---";
                $parts[] = mb_substr($attachment->extracted_text, 0, $attachmentLimit);
            }
        }

        return implode("\n", $parts);
    }

    public function markSent(string $providerMessageId, array $payload = []): void
    {
        $this->forceFill([
            'status' => MessageStatus::SENT,
            'provider_message_id' => $providerMessageId,
            'provider_payload' => array_merge($this->provider_payload ?? [], $payload),
            'sent_at' => $this->sent_at ?? now(),
        ])->save();
    }

    public function markFailed(string $reason, array $payload = []): void
    {
        $this->forceFill([
            'status' => MessageStatus::FAILED,
            'failure_reason' => $reason,
            'provider_payload' => array_merge($this->provider_payload ?? [], $payload),
            'failed_at' => now(),
        ])->save();
    }

    public function markSuppressed(string $reason): void
    {
        $this->forceFill([
            'status' => MessageStatus::SUPPRESSED,
            'failure_reason' => $reason,
            'failed_at' => now(),
        ])->save();
    }

    public function transitionStatus(MessageStatus $next, array $timestamps = []): void
    {
        if ($this->status && $next->rank() < $this->status->rank() && $next->isTerminalFailure() === false) {
            // Don't regress non-failure statuses (e.g. received DELIVERED, then a late SENT).
            if (!empty($timestamps)) {
                $this->forceFill($timestamps)->save();
            }
            return;
        }

        $this->forceFill(array_merge(['status' => $next], $timestamps))->save();
    }
}
