<?php

declare(strict_types=1);

namespace Atdt\Screens;

use Atdt\Ansi;
use Atdt\Ctx;
use Atdt\In;
use Atdt\Messages;
use Atdt\Screen;

/**
 * Private mail (SPEC §10.5): paged letter list, read view (marks is_read),
 * reply via the composer. The list prompt takes a letter number or N/P/Q.
 */
final class MailBoxScreen implements Screen
{
    private const PER_PAGE = 14;
    private const ROW_IN = 22;
    private const ROW_MSG = 24;

    public function describe(): string
    {
        return 'In the mail room';
    }

    public function enter(Ctx $c): array
    {
        $c->set('step', 'list');
        return $this->listPage($c);
    }

    public function input(Ctx $c, In $in): array
    {
        if ((string)$c->get('step') === 'read') {
            if ($in->kind !== 'key') {
                return [$c->bell()];
            }
            return $this->readKey($c, strtoupper((string)$in->key));
        }
        if ($in->kind !== 'line') {
            return [$c->bell()];
        }
        if ($in->line === null) {
            return $c->pop();
        }
        return $this->listPick($c, strtoupper(trim($in->line)));
    }

    // ------------------------------------------------------------------

    /** Inbox by default; (S) flips to the letters you sent (1.5.0). */
    private function sent(Ctx $c): bool
    {
        return (string)$c->get('folder', 'in') === 'sent';
    }

    private function letters(Ctx $c, array $user): array
    {
        return $this->sent($c)
            ? Messages::mailSentBy($c->db(), $user)
            : Messages::mailFor($c->db(), $user);
    }

    private function listPage(Ctx $c): array
    {
        $user = $c->user();
        if ($user === null) {
            return $c->pop();
        }
        $sent = $this->sent($c);
        $letters = $this->letters($c, $user);
        $page = max(0, (int)$c->get('page', 0));
        $pages = max(1, (int)ceil(count($letters) / self::PER_PAGE));
        $page = min($page, $pages - 1);
        $c->set('page', $page);

        $o = $c->out()->cls();
        $o->at(2, 1)->txt($c->center($c->tstr($sent ? 'mail.title_sent' : 'mail.title')));
        $o->at(3, 1)->fg(8)->txt($c->center(str_repeat('─', 50)))->reset();
        if ($letters === []) {
            $o->at(10, 1)->txt($c->center($c->tstr($sent ? 'mail.empty_sent' : 'mail.empty')));
            $mode = $c->promptLine($o, self::ROW_IN, 20, 'mail.pick_empty', 3);
            return [$c->ansi($o->str()), $mode];
        }
        $o->at(5, 6)->txt($c->tstr($sent ? 'mail.columns_sent' : 'mail.columns', ['PG' => ($page + 1) . '/' . $pages]));
        $row = 7;
        $slice = array_slice($letters, $page * self::PER_PAGE, self::PER_PAGE);
        foreach ($slice as $i => $letter) {
            $unread = !$sent && (int)$letter['is_read'] === 0;
            $o->at($row++, 6)->fg($unread ? 15 : 7)->txt(sprintf(
                '%2d) %s %-16s %-38s %s',
                $page * self::PER_PAGE + $i + 1,
                $unread ? '*' : ' ',
                mb_strimwidth((string)($sent ? $letter['to_name'] : $letter['from_name']), 0, 16),
                mb_strimwidth(\Atdt\Pipe::strip(\Atdt\Pipe::escape($c->mci((string)$letter['subject']))), 0, 38),
                $c->localStamp((string)$letter['posted_at'], 'Y-m-d')
            ));
        }
        $o->reset();
        $mode = $c->promptLine($o, self::ROW_IN, 14, 'mail.pick', 3);
        return [$c->ansi($o->str()), $mode];
    }

    private function listPick(Ctx $c, string $pick): array
    {
        if ($pick === '' || $pick === 'Q') {
            return $c->pop();
        }
        if ($pick === 'W') {
            return $c->push('Composer', ['mode' => 'mail']);
        }
        if ($pick === 'S') {
            $c->set('folder', $this->sent($c) ? 'in' : 'sent');
            $c->set('page', 0);
            return $this->listPage($c);
        }
        if ($pick === 'N') {
            $c->set('page', (int)$c->get('page', 0) + 1);
            return $this->listPage($c);
        }
        if ($pick === 'P') {
            $c->set('page', max(0, (int)$c->get('page', 0) - 1));
            return $this->listPage($c);
        }
        $user = $c->user();
        $letters = $user === null ? [] : $this->letters($c, $user);
        $idx = (int)$pick - 1;
        if (!ctype_digit($pick) || !isset($letters[$idx])) {
            $o = Ansi::make()
                ->at(self::ROW_MSG, 1)->txt("\x1b[K")->txt($c->center($c->tstr('mail.bad_pick')))
                ->at(self::ROW_IN, $c->promptEnd(14, 'mail.pick', max: 3))->txt("\x1b[K");
            return [$c->bell(), $c->ansi($o->str()), $c->line($c->promptEnd(14, 'mail.pick', max: 3), self::ROW_IN, 3)];
        }
        $c->set('reading', (int)$letters[$idx]['id']);
        $c->set('rpage', 0);
        $c->set('step', 'read');
        if (!$this->sent($c)) {
            Messages::markMailRead($c->db(), $user, (int)$letters[$idx]['id']);
        }
        return $this->readView($c);
    }

    private function readView(Ctx $c): array
    {
        $letter = Messages::byId($c->db(), (int)$c->get('reading'));
        if ($letter === null) {
            $c->set('step', 'list');
            return $this->listPage($c);
        }
        $o = $c->out()->cls();
        $o->at(1, 2)->fg(8)->txt(str_repeat('─', 78));
        $o->at(2, 4)->txt($c->tstr($this->sent($c) ? 'mail.hdr_to' : 'mail.hdr_from', [
            'FR' => (string)($this->sent($c) ? $letter['to_name'] : $letter['from_name']),
            'DA' => $c->localStamp((string)$letter['posted_at'], 'Y-m-d H:i'),
        ]));
        $o->at(3, 4)->txt($c->tstr('mail.hdr_subject', [
            'SU' => $c->mci((string)$letter['subject']),
        ]));
        $o->at(4, 2)->fg(8)->txt(str_repeat('─', 78))->reset();
        // Wrap the whole letter once, then show the page the caller is on
        // (1.7.0: long letters page with N/P instead of truncating).
        $colors = $c->settings()->bool('allow_user_color', false);
        $lines = [];
        foreach (explode("\n", str_replace("\r", '', (string)$letter['body'])) as $line) {
            $line = rtrim($line);
            foreach (\Atdt\Pipe::wrapVisible($colors ? $line : \Atdt\Pipe::escape($line), 79) as $piece) {
                $lines[] = $piece;
            }
        }
        $perPage = 16;                                  // rows 6..21
        $pages = max(1, (int)ceil(count($lines) / $perPage));
        $page = max(0, min($pages - 1, (int)$c->get('rpage', 0)));
        $c->set('rpage', $page);
        $row = 6;
        foreach (array_slice($lines, $page * $perPage, $perPage) as $piece) {
            $o->at($row++, 2)->fg(7)->txt(\Atdt\Pipe::render($piece, $c->mciVars()));
        }
        if ($pages > 1) {
            $o->reset()->at(self::ROW_IN, 1)->txt("\x1b[K")->txt($c->center($c->tstr('mail.read_more', [
                'PG' => ($page + 1) . '/' . $pages,
            ])));
        }
        $o->reset()->at(self::ROW_MSG, 1)->txt("\x1b[K")->txt($c->center($c->tstr('mail.read_cmds')));
        return [$c->ansi($o->str()), $c->hotkey()];
    }

    private function readKey(Ctx $c, string $key): array
    {
        $letter = Messages::byId($c->db(), (int)$c->get('reading'));
        switch ($key) {
            case 'N':
            case ' ':
                $c->set('rpage', (int)$c->get('rpage', 0) + 1);   // readView clamps
                return $this->readView($c);
            case 'P':
                $c->set('rpage', max(0, (int)$c->get('rpage', 0) - 1));
                return $this->readView($c);
            case 'R':
                if ($letter === null) {
                    return [$c->bell()];
                }
                $c->set('step', 'list');
                // From the Sent folder, R writes the recipient again.
                return $c->push('Composer', [
                    'mode' => 'mail',
                    'to_user_id' => (int)($this->sent($c) ? $letter['to_user_id'] : $letter['from_user_id']),
                    'to_name' => (string)($this->sent($c) ? $letter['to_name'] : $letter['from_name']),
                    'reply_to' => (int)$letter['id'],
                ]);
            case 'B':
                $c->set('step', 'list');
                return $this->listPage($c);
            case 'Q':
            case 'ESC':
                return $c->pop();
            default:
                return [$c->bell()];
        }
    }
}
