<?php

namespace App\Livewire\Admin;

use App\Models\Customer;
use App\Models\InboundMail;
use App\Services\Mail\IngestInboundMail;
use App\Services\Mail\InboundMailbox;
use App\Services\Mail\InboundMailStatus;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;

/**
 * What customers wrote to us.
 *
 * The half that was missing: outgoing mail has a register, portal requests have
 * a page, and a customer who simply replied by mail was invisible here — the
 * question sat in a mail client on somebody's desktop and the console, which
 * knows who they are and what they bought, knew nothing about it.
 *
 * Unassigned mail is shown FIRST and never hidden. A message from an address
 * nobody recognises is a new enquiry or a customer writing from a private
 * account, and it is exactly the one that must not be missed.
 *
 * Attachments are names and sizes. The files are left on the mail server on
 * purpose — storing whatever a stranger chooses to send would make this a
 * malware store with a console in front of it.
 */
#[Layout('layouts.admin')]
class Inbox extends Component
{
    /** Show what has been dealt with as well. */
    #[Url(except: false)]
    public bool $archived = false;

    public function mount(): void
    {
        $this->authorize('customers.manage');
    }

    /**
     * Look now rather than at the next tick.
     *
     * The scheduler polls every couple of minutes; this is for the operator on
     * the phone with somebody who says "ich habe es gerade geschickt".
     */
    public function fetchNow(IngestInboundMail $ingest): void
    {
        $this->authorize('customers.manage');

        $taken = $ingest();

        $this->dispatch('notify', message: $taken === 0
            ? __('inbox.nothing_new')
            : trans_choice('inbox.fetched', $taken, ['count' => $taken]));
    }

    /** Put one away. It stays in the mailbox; only this list forgets it. */
    public function archive(string $uuid): void
    {
        $this->authorize('customers.manage');

        InboundMail::query()->where('uuid', $uuid)->update([
            'archived_at' => now(),
            'read_at' => now(),
        ]);
    }

    public function unarchive(string $uuid): void
    {
        $this->authorize('customers.manage');

        InboundMail::query()->where('uuid', $uuid)->update(['archived_at' => null]);
    }

    /**
     * Attach a mail nobody could place to a customer.
     *
     * The address decides automatically and this is the manual override: a
     * customer who writes from their private account is one an operator
     * recognises and the matcher never will.
     */
    public function assign(string $uuid, string $customerUuid): void
    {
        $this->authorize('customers.manage');

        $mail = InboundMail::query()->where('uuid', $uuid)->first();

        if ($mail === null) {
            return;
        }

        if ($customerUuid === '') {
            $mail->update(['customer_id' => null]);

            return;
        }

        $customer = Customer::query()->where('uuid', $customerUuid)->first();

        if ($customer !== null) {
            $mail->update(['customer_id' => $customer->id]);
            $this->dispatch('notify', message: __('inbox.assigned', ['name' => $customer->name]));
        }
    }

    public function render()
    {
        $this->authorize('customers.manage');

        $mails = InboundMail::query()
            ->with('customer')
            ->when(! $this->archived, fn ($q) => $q->open())
            ->when($this->archived, fn ($q) => $q->whereNotNull('archived_at'))
            // Unassigned first: the mail nobody has placed is the one that gets
            // forgotten, and sorting it under the rest is how it stays that way.
            ->orderByRaw('customer_id IS NULL DESC')
            ->latest('received_at')
            ->limit(100)
            ->get();

        return view('livewire.admin.inbox', [
            'mails' => $mails,
            'customers' => Customer::query()->whereNull('closed_at')->orderBy('name')->get(['uuid', 'name', 'email']),
            'openCount' => InboundMail::query()->open()->count(),
            // Said on the page rather than left as an empty list nobody can
            // explain: a mailbox that was never configured looks exactly like a
            // mailbox with no mail in it.
            'configured' => app(InboundMailbox::class)->isConfigured(),
            // The same line the settings page shows. This is where somebody
            // notices nothing is arriving, so this is where "last reached" has
            // to be readable — otherwise an empty inbox and a broken mailbox
            // look identical.
            'status' => app(InboundMailStatus::class)->last(),
        ]);
    }
}
