// test_ansi_parser.cpp — tests unitarios para fn_term::AnsiParser.
//
// Logica pura: no requiere ImGui ni contexto GL. Cubre:
//   - SGR: reset, FG color, BG color, bright colors, bold
//   - Cursor moves: CUU/CUD/CUF/CUB, CUP
//   - ED(2) erase display, EL(2) erase line
//   - Texto normal + secuencias mixtas
//   - CR, LF, BS

#define CATCH_CONFIG_MAIN
#include "catch_amalgamated.hpp"

#include "core/ansi_parser.h"

#include <string>
#include <vector>

using namespace fn_term;

// Helper: parsea una cadena y colecta los eventos.
static std::vector<AnsiEvent> parse(const std::string& s) {
    AnsiParser p;
    std::vector<AnsiEvent> evs;
    p.feed(s.c_str(), s.size(), [&](const AnsiEvent& ev) {
        evs.push_back(ev);
    });
    return evs;
}

// Helper: obtiene estados SGR después de parsear (sin eventos de salida).
struct SgrState { uint8_t fg; uint8_t bg; uint8_t bold; };
static SgrState parse_sgr(const std::string& s) {
    AnsiParser p;
    p.feed(s.c_str(), s.size(), [](const AnsiEvent&) {});
    return {p.current_fg(), p.current_bg(), p.current_bold()};
}

// ---------------------------------------------------------------------------
// SGR tests
// ---------------------------------------------------------------------------

TEST_CASE("SGR reset sets default colors", "[ansi_parser][sgr]") {
    // Primero ponemos FG rojo, luego reset.
    auto st = parse_sgr("\x1b[31m\x1b[0m");
    REQUIRE(st.fg == kColorDefault);
    REQUIRE(st.bg == kColorDefault);
    REQUIRE(st.bold == 0);
}

TEST_CASE("SGR fg color 31 sets red", "[ansi_parser][sgr]") {
    auto st = parse_sgr("\x1b[31m");
    REQUIRE(st.fg == 1); // rojo = index 1
}

TEST_CASE("SGR bg color 44 sets blue background", "[ansi_parser][sgr]") {
    auto st = parse_sgr("\x1b[44m");
    REQUIRE(st.bg == 4); // azul = index 4
}

TEST_CASE("SGR bright fg 91 sets bright red", "[ansi_parser][sgr]") {
    auto st = parse_sgr("\x1b[91m");
    REQUIRE(st.fg == 9); // bright red = index 8+1 = 9
}

TEST_CASE("SGR bold sets bold flag", "[ansi_parser][sgr]") {
    auto st = parse_sgr("\x1b[1m");
    REQUIRE(st.bold == 1);
}

TEST_CASE("SGR reset via bare ESC[m", "[ansi_parser][sgr]") {
    // ESC [ m sin parametro = reset
    auto st = parse_sgr("\x1b[31m\x1b[m");
    REQUIRE(st.fg == kColorDefault);
}

// ---------------------------------------------------------------------------
// Cursor move tests
// ---------------------------------------------------------------------------

TEST_CASE("cursor CUU moves up N", "[ansi_parser][cursor]") {
    auto evs = parse("\x1b[3A");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::CursorMove);
    REQUIRE(evs[0].cursor_rel.dir == CursorDir::Up);
    REQUIRE(evs[0].cursor_rel.n == 3);
}

TEST_CASE("cursor CUF moves forward N", "[ansi_parser][cursor]") {
    auto evs = parse("\x1b[5C");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::CursorMove);
    REQUIRE(evs[0].cursor_rel.dir == CursorDir::Forward);
    REQUIRE(evs[0].cursor_rel.n == 5);
}

TEST_CASE("cursor CUB moves back 1 when no param", "[ansi_parser][cursor]") {
    auto evs = parse("\x1b[D");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::CursorMove);
    REQUIRE(evs[0].cursor_rel.dir == CursorDir::Back);
    REQUIRE(evs[0].cursor_rel.n == 1);
}

TEST_CASE("cursor CUP absolute position", "[ansi_parser][cursor]") {
    // ESC[5;10H → row=4, col=9 (0-based)
    auto evs = parse("\x1b[5;10H");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::CursorAbsolute);
    REQUIRE(evs[0].cursor_abs.row == 4);
    REQUIRE(evs[0].cursor_abs.col == 9);
}

TEST_CASE("cursor CUP default params (ESC[H) = origin", "[ansi_parser][cursor]") {
    auto evs = parse("\x1b[H");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::CursorAbsolute);
    REQUIRE(evs[0].cursor_abs.row == 0);
    REQUIRE(evs[0].cursor_abs.col == 0);
}

// ---------------------------------------------------------------------------
// Erase tests
// ---------------------------------------------------------------------------

TEST_CASE("erase display ED 2", "[ansi_parser][erase]") {
    auto evs = parse("\x1b[2J");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::EraseDisplay);
}

TEST_CASE("erase line EL 2", "[ansi_parser][erase]") {
    auto evs = parse("\x1b[2K");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::EraseLine);
}

// ---------------------------------------------------------------------------
// Control chars
// ---------------------------------------------------------------------------

TEST_CASE("newline and carriage return", "[ansi_parser][control]") {
    auto evs = parse("\r\n");
    REQUIRE(evs.size() == 2);
    REQUIRE(evs[0].type == AnsiEventType::CarriageReturn);
    REQUIRE(evs[1].type == AnsiEventType::Newline);
}

TEST_CASE("backspace emits Backspace event", "[ansi_parser][control]") {
    auto evs = parse("\x08");
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].type == AnsiEventType::Backspace);
}

// ---------------------------------------------------------------------------
// Text + mixed sequences
// ---------------------------------------------------------------------------

TEST_CASE("plain text emits Char events", "[ansi_parser][text]") {
    auto evs = parse("hi");
    REQUIRE(evs.size() == 2);
    REQUIRE(evs[0].type == AnsiEventType::Char);
    REQUIRE(evs[0].cell.ch == U'h');
    REQUIRE(evs[1].cell.ch == U'i');
}

TEST_CASE("mixed text and SGR sequence", "[ansi_parser][mixed]") {
    // "A" con FG rojo, luego reset, luego "B".
    auto evs = parse("\x1b[31mA\x1b[0mB");
    // Debemos tener exactamente 2 eventos Char: A (fg=1) y B (fg=default).
    REQUIRE(evs.size() == 2);
    REQUIRE(evs[0].type == AnsiEventType::Char);
    REQUIRE(evs[0].cell.ch == U'A');
    REQUIRE(evs[0].cell.fg == 1); // rojo
    REQUIRE(evs[1].type == AnsiEventType::Char);
    REQUIRE(evs[1].cell.ch == U'B');
    REQUIRE(evs[1].cell.fg == kColorDefault);
}

TEST_CASE("char inherits current SGR attrs", "[ansi_parser][sgr]") {
    AnsiParser p;
    std::vector<AnsiEvent> evs;
    // Poner BG azul, luego emitir texto.
    std::string s = "\x1b[44mX";
    p.feed(s.c_str(), s.size(), [&](const AnsiEvent& ev) { evs.push_back(ev); });
    REQUIRE(evs.size() == 1);
    REQUIRE(evs[0].cell.ch == U'X');
    REQUIRE(evs[0].cell.bg == 4); // azul
}

TEST_CASE("unknown CSI final byte ignored silently", "[ansi_parser][robustness]") {
    // ESC [ Z es desconocido — no debe emitir nada ni crashear.
    auto evs = parse("a\x1b[Zb");
    REQUIRE(evs.size() == 2);
    REQUIRE(evs[0].cell.ch == U'a');
    REQUIRE(evs[1].cell.ch == U'b');
}

TEST_CASE("incomplete escape at end of buffer", "[ansi_parser][robustness]") {
    // Buffer termina a mitad de una secuencia — no debe crashear.
    AnsiParser p;
    std::string s1 = "\x1b[3";
    std::string s2 = "1m";
    p.feed(s1.c_str(), s1.size(), [](const AnsiEvent&) {});
    p.feed(s2.c_str(), s2.size(), [](const AnsiEvent&) {});
    REQUIRE(p.current_fg() == 1); // FG rojo aplicado correctamente
}

TEST_CASE("reset() clears state", "[ansi_parser][reset]") {
    AnsiParser p;
    std::string s = "\x1b[31m"; // FG rojo
    p.feed(s.c_str(), s.size(), [](const AnsiEvent&) {});
    REQUIRE(p.current_fg() == 1);
    p.reset();
    REQUIRE(p.current_fg() == kColorDefault);
}
