<?php

declare(strict_types=1);

namespace Atdt\Screens;

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

/**
 * Page the sysop, the classic way (SPEC §10.6, owner round 2): reason →
 * the board SITS THERE ringing the back-room bell — animated dots, a
 * periodic BEL, an any-key abort — until the sysop answers from the
 * console (the caller lands straight in chat with them), or ~90 seconds
 * pass (the page stays lit on the console either way). api.php drives
 * the wait through onPoll() on every poll tick.
 */
final class PageSysopScreen implements Screen
{
    public const TIMEOUT = 90;   // seconds of ringing before giving up
    private const ROW_RING = 13;
    private const ROW_MOOD = 15;
    private const ROW_HINT = 17;

    public function describe(): string
    {
        return 'Paging the sysop';
    }

    public function enter(Ctx $c): array
    {
        $c->set('step', 'reason');
        $o = $c->out()->cls();
        $o->at(3, 1)->txt($c->center($c->tstr('page.title')));
        $o->at(4, 1)->fg(8)->txt($c->center(str_repeat('─', 40)))->reset();
        $o->at(7, 10)->txt($c->tstr('page.blurb'));
        $mode = $c->promptLine($o, 10, 10, 'page.ask', Chat::MAX_BODY);
        return [$c->ansi($o->str()), $mode];
    }

    public function input(Ctx $c, In $in): array
    {
        $step = (string)$c->get('step', 'reason');
        if ($step === 'paging' || $step === 'done') {
            // Any input stops waiting; the page row stays for the console.
            return $c->pop();
        }
        if ($in->kind !== 'line') {
            return [$c->bell()];
        }
        $reason = Chat::clean((string)$in->line);
        if ($in->line === null || $reason === '') {
            return $c->pop();
        }
        $around = Chat::page(
            $c->db(),
            $c->settings(),
            $c->node(),
            (string)(($c->user() ?? [])['handle'] ?? 'caller'),
            $reason
        );
        $c->set('step', 'paging');
        $c->set('paged_at', time());
        $o = Ansi::make();
        $o->at(self::ROW_RING, 18)->txt($c->tstr('page.ringing'))->txt('.');
        $o->at(self::ROW_MOOD, 1)->txt($c->center($c->tstr($around ? 'page.here_hint' : 'page.away_hint')));
        $o->at(self::ROW_HINT, 1)->txt($c->center($c->tstr('page.wait_hint')));
        return [$c->bell(), $c->ansi($o->str()), $c->hotkey()];
    }

    /**
     * Poll tick while the caller waits (api.php, §9.6). Delivered rows that
     * carry a sysop line for this node mean the sysop picked up: the caller
     * drops straight into chat with the reply on screen.
     * @param list<array<string, mixed>> $rows
     */
    public function onPoll(Ctx $c, array $rows): array
    {
        // The sysop picking up beats every step (1.5.5): mid-reason, mid-ring,
        // or long after the ring timed out. It used to count only while
        // 'paging', so a late pickup (or a break-in while the caller typed
        // their reason) was silently eaten by the watermark and the line
        // only opened once the caller wandered off and a LATER console
        // line broke in -- the wonkiness the owner reported.
        foreach ($rows as $row) {
            if ((string)$row['kind'] === 'sysop' && (int)($row['to_node'] ?? 0) === $c->node()) {
                // The private line opens with the answer already on screen.
                $frames = $c->swap('SysopChat', ['note' => 'answered']);
                array_unshift($frames, $c->bell());
                return $frames;
            }
        }
        if ((string)$c->get('step') !== 'paging') {
            return [];
        }
        $elapsed = max(0, time() - (int)$c->get('paged_at', time()));
        if ($elapsed >= self::TIMEOUT) {
            $c->set('step', 'done');
            $o = Ansi::make();
            $o->at(self::ROW_RING, 1)->txt("\x1b[K");
            $o->at(self::ROW_RING, 1)->txt($c->center($c->tstr('page.timeout')));
            $o->at(self::ROW_MOOD, 1)->txt("\x1b[K");
            $o->at(self::ROW_HINT, 1)->txt("\x1b[K")->txt($c->center($c->tstr('page.timeout_keys')));
            return [$c->bell(), $c->ansi($o->str())];
        }
        // Still ringing: grow the dots, re-ring the bell every ~12s.
        $o = Ansi::make()->at(self::ROW_RING, 1)->txt("\x1b[K");
        $o->at(self::ROW_RING, 18)->txt($c->tstr('page.ringing'))
            ->txt(str_repeat('.', 1 + intdiv($elapsed, 4) % 6));
        $frames = [$c->ansi($o->str())];
        if ($elapsed >= 12 && $elapsed % 12 < 4) {
            $frames[] = $c->bell();
        }
        return $frames;
    }
}
