<?php

declare(strict_types=1);

namespace Atdt\Screens;

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

/**
 * Compose a public post, a threaded reply, or a private letter (SPEC §10.5).
 * Subject rides line mode (replies pre-fill "Re:"); the body uses the §7.5
 * local editor, with quote lines supplied for replies (^Q).
 *
 * d: {mode: post|reply|mail, area_id?, reply_to?, to_user_id?, to_name?}
 */
final class ComposerScreen implements Screen
{
    private const ROW_TO = 8;
    private const ROW_SUBJ = 10;
    // Row 25: the underlying screen's command footer (reader row 24, list
    // row 23) must stay visible next to the posted/aborted notice.
    private const ROW_MSG = 25;
    private const COL_LABEL = 10;
    private const COL_INPUT = 24;

    public function describe(): string
    {
        return 'Writing a message';
    }

    public function enter(Ctx $c): array
    {
        $mode = (string)$c->get('mode', 'post');
        $o = $c->out()->cls();
        $o->at(2, 1)->txt($c->center($c->tstr('compose.title_' . $mode)));
        $o->at(3, 1)->fg(8)->txt($c->center(str_repeat('─', 40)))->reset();

        if ($mode === 'mail' && $c->get('to_user_id') === null) {
            $c->set('step', 'to');
            $o->at(5, self::COL_LABEL)->txt($c->tstr('compose.hint_esc'));
            $o->at(self::ROW_TO, self::COL_LABEL)->txt($c->tstr('compose.ask_to'));
            return [$c->ansi($o->str()), $c->line(self::COL_INPUT, self::ROW_TO, 20)];
        }
        return $this->askSubject($c, $o);
    }

    public function input(Ctx $c, In $in): array
    {
        if ($in->kind === 'editor') {
            return $this->editorDone($c, $in->lines);
        }
        if ($in->kind !== 'line') {
            return [$c->bell()];
        }
        if ($in->line === null) {
            return $this->abort($c);
        }
        return match ($c->get('step')) {
            'to' => $this->toEntered($c, trim($in->line)),
            'subject' => $this->subjectEntered($c, trim($in->line)),
            default => $this->abort($c),
        };
    }

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

    private function askSubject(Ctx $c, ?Ansi $o = null): array
    {
        $mode = (string)$c->get('mode', 'post');
        $c->set('step', 'subject');
        $o ??= $c->out();
        if ($mode !== 'mail') {
            $area = $this->area($c);
            if ($area === null) {
                return $this->abort($c);
            }
            $c->set('area_id', (int)$area['id']);
            $o->at(5, self::COL_LABEL)->txt($c->tstr('compose.in_area', ['AR' => (string)$area['name']]));
        } else {
            $o->at(5, self::COL_LABEL)->txt($c->tstr('compose.to_line', ['TO' => (string)$c->get('to_name')]));
        }
        $initial = '';
        $reply = $this->replyTarget($c);
        if ($reply !== null) {
            $initial = (string)$reply['subject'];
            if (!str_starts_with(strtolower($initial), 're:')) {
                $initial = 'Re: ' . $initial;
            }
            $initial = mb_strimwidth($initial, 0, 60);
        }
        $o->at(self::ROW_SUBJ, self::COL_LABEL)->txt($c->tstr('compose.ask_subject'));
        return [$c->ansi($o->str()), $c->line(self::COL_INPUT, self::ROW_SUBJ, 60, initial: $initial)];
    }

    private function toEntered(Ctx $c, string $handle): array
    {
        if ($handle === '') {
            return $this->abort($c);
        }
        $to = Users::findByHandle($c->db(), $handle);
        if ($to === null || $to['status'] === 'deleted') {
            $o = $c->out()
                ->at(self::ROW_MSG, 1)->txt("\x1b[K")->txt($c->center($c->tstr('compose.no_such_user')))
                ->at(self::ROW_TO, self::COL_INPUT)->txt("\x1b[K");
            return [$c->ansi($o->str()), $c->line(self::COL_INPUT, self::ROW_TO, 20)];
        }
        $c->set('to_user_id', (int)$to['id']);
        $c->set('to_name', (string)$to['handle']);
        $o = $c->out()->at(self::ROW_MSG, 1)->txt("\x1b[K");
        return $this->askSubject($c, $o);
    }

    private function subjectEntered(Ctx $c, string $subject): array
    {
        $c->set('subject', $subject);
        $c->set('step', 'editor');
        $mode = (string)$c->get('mode', 'post');
        $reply = $this->replyTarget($c);
        $title = \Atdt\Pipe::strip($c->tstr('compose.editor_title_' . $mode, [
            'AR' => $mode === 'mail' ? (string)$c->get('to_name') : (string)($this->area($c)['name'] ?? ''),
        ]));
        $frame = [
            't' => 'mode', 'm' => 'editor',
            'rows' => 23, 'cols' => 79,
            'initial' => [],
            'title' => $title,
        ];
        if ($reply !== null) {
            $frame['quote'] = Messages::quoteLines($reply); // §7.5 ^Q source
        }
        return [$frame];
    }

    /** @param ?list<string> $lines */
    private function editorDone(Ctx $c, ?array $lines): array
    {
        if ($lines === null || $this->linesEmpty($lines)) {
            return $this->abort($c);
        }
        $user = $c->user();
        if ($user === null) {
            return $this->abort($c);
        }
        $mode = (string)$c->get('mode', 'post');
        $reply = $this->replyTarget($c);
        if ($mode === 'mail') {
            $to = $c->db()->one('SELECT * FROM users WHERE id = ?', [(int)$c->get('to_user_id')]);
            if ($to === null) {
                return $this->abort($c);
            }
            $res = Messages::sendMail(
                $c->db(),
                $c->settings(),
                $user,
                $to,
                (string)$c->get('subject'),
                $lines,
                $reply
            );
            $noticeKey = 'compose.sent_mail';
        } else {
            $res = Messages::post(
                $c->db(),
                $c->settings(),
                (int)$c->get('area_id'),
                $user,
                'All',
                null,
                (string)$c->get('subject'),
                $lines,
                $reply
            );
            $noticeKey = 'compose.posted';
        }
        Messages::recordPost($c->db(), $user, $c->session());
        $c->refreshUser();
        $frames = $c->pop();
        $frames[] = $c->ansi(
            Ansi::make()->at(self::ROW_MSG, 1)->txt("\x1b[K")
                ->txt($c->center($c->tstr($noticeKey, ['MN' => (string)$res['num']])))->str()
        );
        return $frames;
    }

    private function abort(Ctx $c): array
    {
        $frames = $c->pop();
        $frames[] = $c->ansi(
            Ansi::make()->at(self::ROW_MSG, 1)->txt("\x1b[K")
                ->txt($c->center($c->tstr('compose.aborted')))->str()
        );
        return $frames;
    }

    private function area(Ctx $c): ?array
    {
        $id = $c->get('area_id');
        if ($id !== null) {
            return Messages::area($c->db(), (int)$id);
        }
        return Messages::currentArea($c->db(), $c->user());
    }

    private function replyTarget(Ctx $c): ?array
    {
        $id = $c->get('reply_to');
        return $id === null ? null : Messages::byId($c->db(), (int)$id);
    }

    /** @param list<string> $lines */
    private function linesEmpty(array $lines): bool
    {
        foreach ($lines as $line) {
            if (trim((string)$line) !== '') {
                return false;
            }
        }
        return true;
    }
}
