<?php

declare(strict_types=1);

namespace Atdt\Screens;

use Atdt\Ansi;
use Atdt\Ctx;
use Atdt\In;
use Atdt\Mailer;
use Atdt\Pipe;
use Atdt\Screen;
use Atdt\Users;

/**
 * The EMAILSYSOP menu action (1.5.0): a caller writes a subject and a
 * full-screen-editor body, and the board emails it to the address the
 * item names, sent from the account configured in Settings. The
 * caller's stored email rides along as Reply-To so the SysOp answers
 * from a normal inbox. One letter per caller per board day.
 */
final class EmailSysopScreen implements Screen
{
    private const ROW_IN = 12;

    public function describe(): string
    {
        return 'Writing to the SysOp';
    }

    public function enter(Ctx $c): array
    {
        if (!Mailer::ready($c->settings())) {
            return $this->bail($c, 'esys.mail_off'); // hidden item pressed blind
        }
        $u = $c->user();
        if ($u === null) {
            return $c->pop();
        }
        if ((string)Users::getPref($c->db(), $u, 'esys_day', '') === $c->boardDayStartUtc()) {
            return $this->bail($c, 'esys.rate');
        }
        $c->set('step', 'subject');
        $o = $c->out()->cls();
        $o->at(2, 1)->txt($c->center($c->tstr('esys.title')));
        $o->at(3, 1)->fg(8)->txt($c->center(str_repeat('─', 50)))->reset();
        $o->at(6, 8)->txt($c->tstr('esys.blurb1'));
        $o->at(7, 8)->txt($c->tstr('esys.blurb2'));
        $o->at(self::ROW_IN - 1, 8)->txt($c->tstr('esys.ask_subject'));
        return [$c->ansi($o->str()), $c->line(8, self::ROW_IN, 60)];
    }

    public function input(Ctx $c, In $in): array
    {
        if ($in->kind === 'line') {
            $subject = trim((string)($in->line ?? ''));
            if ($in->line === null || $subject === '') {
                return $c->pop();
            }
            $c->set('subject', mb_strimwidth($subject, 0, 60));
            $c->set('step', 'body');
            return [[
                't' => 'mode', 'm' => 'editor',
                'rows' => 10, 'cols' => 79,
                'initial' => [],
                'title' => Pipe::strip($c->tstr('esys.editor_title')),
            ]];
        }
        if ($in->kind === 'editor') {
            if ($in->lines === null || trim(implode('', $in->lines)) === '') {
                return $c->pop(); // aborted or empty: nothing to send
            }
            return $this->send($c, $in->lines);
        }
        return [$c->bell()];
    }

    /** @param list<string> $lines */
    private function send(Ctx $c, array $lines): array
    {
        $u = $c->user() ?? [];
        $to = trim((string)$c->get('to', ''));
        $board = (string)($c->settings()->get('board_name', 'ATDT') ?? 'ATDT');
        $callerMail = trim((string)($u['email'] ?? ''));
        $body = 'From caller: ' . (string)($u['handle'] ?? 'caller')
            . ' (level ' . (int)($u['level'] ?? 0)
            . ', member since ' . $c->localStamp((string)($u['created_at'] ?? now_utc()), 'Y-m-d') . ")\n"
            . 'Board: ' . $board . "\n"
            . 'Caller email: ' . ($callerMail === '' ? 'none on file' : $callerMail) . "\n"
            . str_repeat('-', 60) . "\n\n"
            . Pipe::strip(implode("\n", array_map(fn($l) => rtrim((string)$l), $lines)));

        $err = Mailer::send(
            $c->db(),
            $c->settings(),
            $to,
            '[' . $board . '] ' . (string)$c->get('subject', 'a caller writes'),
            $body,
            Mailer::plausible($callerMail) ? $callerMail : ''
        );
        if ($err === null && $c->user() !== null) {
            Users::setPref($c->db(), $c->user(), 'esys_day', $c->boardDayStartUtc());
            $c->refreshUser(); // the rate limit must bite within this same call
        }
        return $this->bail($c, $err === null ? 'esys.sent' : 'esys.fail');
    }

    /** Pop back to the menu with one line of outcome under it. */
    private function bail(Ctx $c, string $stringKey): array
    {
        $frames = $c->pop();
        $frames[] = $c->ansi(
            Ansi::make()->at(24, 1)->txt("\x1b[K")
                ->txt($c->center($c->tstr($stringKey)))->str()
        );
        return $frames;
    }
}
