<?php

namespace App\Models;

use App\Models\Concerns\HasUuid;
use App\Services\Secrets\SecretCipher;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
 * One sending address.
 *
 * The password is stored under SECRETS_KEY rather than APP_KEY, and therefore
 * not through Laravel's `encrypted` cast — that cast uses APP_KEY, which is
 * rotated as ordinary maintenance and would take every mailbox with it.
 */
class Mailbox extends Model
{
    use HasFactory, HasUuid;

    protected $fillable = [
        'key', 'address', 'display_name', 'username',
        'password', 'no_reply', 'active', 'authenticates', 'last_verified_at',
    ];

    protected function casts(): array
    {
        return [
            'no_reply' => 'boolean',
            'active' => 'boolean',
            'authenticates' => 'boolean',
            'last_verified_at' => 'datetime',
        ];
    }

    public static function findByKey(string $key): ?self
    {
        return static::query()->where('key', $key)->first();
    }

    /** Plaintext in, ciphertext to the column. */
    public function setPasswordAttribute(?string $value): void
    {
        $this->attributes['password'] = ($value === null || $value === '')
            ? null
            : app(SecretCipher::class)->encrypt($value);
    }

    /** Ciphertext from the column, plaintext out. */
    public function getPasswordAttribute(?string $value): ?string
    {
        return ($value === null || $value === '')
            ? null
            : app(SecretCipher::class)->decrypt($value);
    }

    /** The SMTP user: an explicit one, or the address itself. */
    public function smtpUsername(): string
    {
        return $this->username !== null && $this->username !== ''
            ? $this->username
            : $this->address;
    }

    /**
     * last_verified_at proves a successful test against the config THIS
     * mailbox held at the time — an address, username or authenticates
     * change (or a new password) invalidates only this row. Sets the
     * attribute without saving: EditMailbox::save() calls this alongside
     * other attribute changes so the whole edit lands in one write, not two.
     */
    public function invalidateVerification(): void
    {
        $this->last_verified_at = null;
    }

    /**
     * The mirror of invalidateVerification() for the server card
     * (Admin\Mail::saveServer()): host, port and encryption are shared by
     * every mailbox at once, not a per-row concept, so a change there
     * invalidates all of them in one write rather than each row deciding
     * independently — Codex R15#6, P2. Scoped to rows that still have
     * something to clear, so saving unrelated server settings does not bump
     * every mailbox's updated_at for nothing.
     */
    public static function invalidateAllVerifications(): int
    {
        return static::query()->whereNotNull('last_verified_at')->update(['last_verified_at' => null]);
    }

    /**
     * Enough to send with: an address, and a password ONLY when this mailbox
     * actually authenticates.
     *
     * A trusted local or private-network relay can legitimately need no
     * password at all (Codex R15#4, P1b) — requiring one unconditionally
     * would refuse a relay that was never broken, just unauthenticated.
     */
    public function isConfigured(): bool
    {
        return $this->active
            && $this->address !== ''
            && (! $this->authenticates || $this->getRawOriginal('password') !== null);
    }
}
