<?php

declare(strict_types=1);

namespace Atdt\Screens;

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

/**
 * Oneliners (SPEC §10.6): the last 15, add-yours (one line ≤ 70 chars,
 * one per caller per board-day).
 */
final class OnelinersScreen implements Screen
{
    private const ROW_IN = 22;
    private const ROW_MSG = 24;

    public function describe(): string
    {
        return 'Reading the oneliners';
    }

    public function enter(Ctx $c): array
    {
        $c->set('step', 'list');
        $rows = array_reverse($c->db()->all('SELECT * FROM oneliners ORDER BY id DESC LIMIT 15'));
        $o = $c->out()->cls();
        $o->at(2, 1)->txt($c->center($c->tstr('one.title')));
        $o->at(3, 1)->txt($c->center($c->tstr('one.blurb')));
        if ($rows === []) {
            $o->at(10, 1)->txt($c->center($c->tstr('one.empty')));
        }
        $colors = $c->settings()->bool('allow_user_color', false);
        // A long line wraps instead of losing its tail (1.6.0), and the
        // date carries the year. Wrapping costs rows, so when the window
        // fills, the OLDEST entries yield - the wall always shows the
        // freshest writing whole.
        $wrapped = [];
        foreach ($rows as $r) {
            $wrapped[] = ChatScreen::wrap(
                '|08' . $c->localStamp((string)$r['at'], 'm-d-y') . ' |11'
                . Pipe::escape((string)$r['handle']) . '|08: |07'
                . ($colors ? (string)$r['body'] : Pipe::escape((string)$r['body'])),
                77,
                3
            );
        }
        $budget = 16; // rows 5..20; the prompt keeps row 21
        $show = [];
        for ($i = count($wrapped) - 1; $i >= 0; $i--) {
            if ($budget - count($wrapped[$i]) < 0) {
                break;
            }
            $budget -= count($wrapped[$i]);
            array_unshift($show, $wrapped[$i]);
        }
        $row = 5;
        foreach ($show as $lines) {
            foreach ($lines as $line) {
                $o->at($row++, 3)->txt($c->pipe($line));
            }
        }
        $o->at(21, 1)->txt($c->center($c->tstr('one.prompt')));
        return [$c->ansi($o->str()), $c->hotkey()];
    }

    public function input(Ctx $c, In $in): array
    {
        if ($in->kind === 'line') {
            return $this->lineEntered($c, $in->line);
        }
        if ($in->kind !== 'key' || $c->get('step') !== 'list') {
            return [$c->bell()];
        }
        switch (strtoupper((string)$in->key)) {
            case 'A':
                $u = $c->user();
                $already = $u === null ? 0 : (int)$c->db()->val(
                    'SELECT COUNT(*) FROM oneliners WHERE user_id = ? AND at >= ?',
                    [(int)$u['id'], $c->boardDayStartUtc()]
                );
                if ($u === null || $already > 0) {
                    return $this->notice($c, 'one.rate');
                }
                // The prompt gets its own row so the whole 79 columns fit
                // (1.5.0: the shared-row field cut long lines short).
                $c->set('step', 'add');
                $o = Ansi::make()
                    ->at(self::ROW_IN - 1, 1)->txt("\x1b[K")
                    ->at(self::ROW_IN, 1)->txt("\x1b[K")
                    ->at(self::ROW_IN - 1, 3)->txt($c->tstr('one.ask'));
                return [$c->ansi($o->str()), $c->line(2, self::ROW_IN, 79)];
            case 'Q':
            case 'ESC':
                return $c->pop();
            case '?':
                return $this->enter($c);
            default:
                return [$c->bell()];
        }
    }

    /** @param ?string $line */
    private function lineEntered(Ctx $c, ?string $line): array
    {
        $c->set('step', 'list');
        $body = \Atdt\Chat::clean((string)$line, 79);
        if ($line === null || $body === '') {
            return $this->enter($c);
        }
        $u = $c->user();
        $c->db()->insert('oneliners', [
            'user_id' => (int)($u['id'] ?? 0),
            'handle' => (string)($u['handle'] ?? 'caller'),
            'body' => $body,
            'at' => now_utc(),
        ]);
        $frames = $this->enter($c);
        $frames[] = $c->ansi(Ansi::make()->at(self::ROW_MSG, 1)->txt("\x1b[K")
            ->txt($c->center($c->tstr('one.added')))->str());
        return $frames;
    }

    private function notice(Ctx $c, string $key): array
    {
        return [
            $c->bell(),
            $c->ansi(Ansi::make()->at(self::ROW_MSG, 1)->txt("\x1b[K")
                ->txt($c->center($c->tstr($key)))->str()),
            $c->hotkey(),
        ];
    }
}
