<?php

namespace App\Models\Mail;

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

/**
 * Tek bir e-posta. Başlıklar senkronda, gövde ilk açılışta çekilir.
 */
class MailMessage extends Model
{
    protected $fillable = [
        'account_id', 'folder_id', 'customer_id', 'uid', 'message_id', 'in_reply_to', 'references_json', 'thread_key',
        'subject', 'from_name', 'from_email', 'to_json', 'cc_json', 'bcc_json', 'reply_to_json',
        'date_at', 'snippet', 'body_html', 'body_text', 'body_fetched_at',
        'size', 'has_attachments',
        'is_seen', 'is_flagged', 'is_answered', 'is_forwarded', 'is_draft', 'is_deleted',
        'priority', 'headers_json',
    ];

    protected $casts = [
        'references_json' => 'array',
        'to_json' => 'array',
        'cc_json' => 'array',
        'bcc_json' => 'array',
        'reply_to_json' => 'array',
        'headers_json' => 'array',
        'date_at' => 'datetime',
        'body_fetched_at' => 'datetime',
        'has_attachments' => 'boolean',
        'is_seen' => 'boolean',
        'is_flagged' => 'boolean',
        'is_answered' => 'boolean',
        'is_forwarded' => 'boolean',
        'is_draft' => 'boolean',
        'is_deleted' => 'boolean',
        'size' => 'integer',
        'priority' => 'integer',
    ];

    public function account(): BelongsTo
    {
        return $this->belongsTo(MailAccount::class, 'account_id');
    }

    public function folder(): BelongsTo
    {
        return $this->belongsTo(MailFolder::class, 'folder_id');
    }

    /** Bağlı CRM müşteri kartı (adres eşleşmesiyle kurulur). */
    public function customer(): BelongsTo
    {
        return $this->belongsTo(\App\Models\Customer\Customer::class, 'customer_id');
    }

    /**
     * Elle kurulmuş / zincirden türemiş kayıt bağları.
     *
     * `customer_id` tek bir müşteriyi adres eşleşmesiyle taşır; bu ilişki ise
     * iletinin satış, operasyon, tedarikçi gibi birden çok kayda bağlanmasını
     * sağlar (bkz. MailLinkService).
     */
    public function links(): HasMany
    {
        return $this->hasMany(MailLink::class, 'message_id');
    }

    /** Ham (RFC 822) yedek — dışa aktarmanın kaynağı. */
    public function archive(): \Illuminate\Database\Eloquent\Relations\HasOne
    {
        return $this->hasOne(MailArchive::class, 'message_id');
    }

    public function attachments(): HasMany
    {
        return $this->hasMany(MailAttachment::class, 'message_id');
    }

    public function scopeForCustomer(Builder $q, int $customerId): Builder
    {
        return $q->where('customer_id', $customerId);
    }

    public function scopeVisible(Builder $q): Builder
    {
        return $q->where('mail_messages.is_deleted', false);
    }

    /** Tam metin indeksi bulunduğunda tek istek boyunca hatırlanır. */
    private static ?bool $fulltextIndex = null;

    /**
     * Arama süzgecini sorguya uygular.
     *
     * Gövde metni LONGTEXT olduğundan "%terim%" araması hiçbir indeksi
     * kullanamaz ve ileti sayısı büyüdükçe tam tablo taramasına döner. MySQL
     * tarafında tam metin indeksi varsa arama ondan yapılır; diğer sürücülerde
     * ve tam metne uygun olmayan terimlerde (kısa sözcük, joker karakter)
     * LIKE'a düşülür.
     */
    public static function applySearch(Builder $query, string $term): Builder
    {
        $term = trim($term);
        if ($term === '') {
            return $query;
        }

        if ($boolean = self::booleanTerm($term)) {
            return $query->whereRaw(
                'MATCH (mail_messages.subject, mail_messages.from_name, mail_messages.from_email,'
                .' mail_messages.snippet, mail_messages.body_text) AGAINST (? IN BOOLEAN MODE)',
                [$boolean]
            );
        }

        $like = '%'.str_replace(['%', '_'], ['\%', '\_'], $term).'%';

        return $query->where(function (Builder $q) use ($like) {
            $q->where('mail_messages.subject', 'like', $like)
                ->orWhere('mail_messages.from_name', 'like', $like)
                ->orWhere('mail_messages.from_email', 'like', $like)
                ->orWhere('mail_messages.snippet', 'like', $like)
                ->orWhere('mail_messages.body_text', 'like', $like);
        });
    }

    /**
     * Terimi BOOLEAN MODE sorgusuna çevirir; uygun değilse null döner.
     *
     * Kullanıcının yazdığı operatörler (+, -, *, ", ~, <, >) sorguya
     * geçirilmez — hem sözdizimi hatasını hem de beklenmedik sonucu önler.
     */
    private static function booleanTerm(string $term): ?string
    {
        if (! self::hasFulltextIndex()) {
            return null;
        }

        // InnoDB'de varsayılan en küçük sözcük uzunluğu 3'tür; daha kısa
        // aramalarda tam metin hiçbir şey bulmaz, LIKE doğru sonucu verir.
        $words = collect(preg_split('/[^\p{L}\p{N}_@.]+/u', $term, -1, PREG_SPLIT_NO_EMPTY) ?: [])
            ->filter(fn ($w) => mb_strlen($w) >= 3)
            ->values();

        if ($words->isEmpty()) {
            return null;
        }

        return $words->map(fn ($w) => '+'.$w.'*')->implode(' ');
    }

    private static function hasFulltextIndex(): bool
    {
        if (self::$fulltextIndex !== null) {
            return self::$fulltextIndex;
        }

        try {
            $connection = \Illuminate\Support\Facades\DB::connection();

            if ($connection->getDriverName() !== 'mysql') {
                return self::$fulltextIndex = false;
            }

            $rows = $connection->select(
                "SHOW INDEX FROM mail_messages WHERE Key_name = 'mail_messages_search_ft'"
            );

            return self::$fulltextIndex = ! empty($rows);
        } catch (\Throwable) {
            return self::$fulltextIndex = false;
        }
    }

    /** Listede gösterilecek taraf: Gönderilenler/Taslaklar'da alıcı, diğerlerinde gönderen. */
    public function counterparty(): array
    {
        $folderType = $this->folder?->type;

        if (in_array($folderType, ['sent', 'drafts'], true)) {
            $to = $this->to_json ?: [];
            $first = $to[0] ?? null;
            $extra = max(0, count($to) - 1);
            $name = $first['name'] ?? $first['email'] ?? '(alıcı yok)';

            return [
                'name' => $extra > 0 ? $name.' +'.$extra : $name,
                'email' => $first['email'] ?? '',
            ];
        }

        return [
            'name' => $this->from_name ?: ($this->from_email ?: '(bilinmiyor)'),
            'email' => $this->from_email ?: '',
        ];
    }

    /** CRM akışında yön: giden (bizden) mi gelen mi? */
    public function direction(): string
    {
        return in_array($this->folder?->type, ['sent', 'drafts'], true) ? 'outbound' : 'inbound';
    }

    /** Avatar baş harfi. */
    public function initial(): string
    {
        $src = $this->counterparty()['name'];

        return mb_strtoupper(mb_substr(trim($src) ?: '?', 0, 1));
    }

    /** Konu başlığından Re:/Fwd: önekleri arındırılmış sohbet anahtarı. */
    public static function threadKeyFor(?string $subject): string
    {
        $s = mb_strtolower(trim((string) $subject));
        $s = preg_replace('/^(\s*(re|ynt|yan|fwd|fw|ilt|iletildi)\s*(\[\d+\])?\s*:\s*)+/iu', '', $s) ?? $s;

        return mb_substr(trim($s), 0, 180);
    }
}
