#pragma once
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <string>
#include <vector>

// Device-side view model for the read-only Agent History. It is deliberately
// small and host-testable: the firmware never stores a second conversation
// database and never keeps more than the bounded cache in agent_history.h.
namespace wearable {

// Hard protocol bounds. A page that exceeds either is rejected, never truncated
// silently on the wire.
constexpr size_t HISTORY_MAX_MESSAGES = 4;
constexpr size_t HISTORY_MAX_FRAME_BYTES = 4096;
// Bound for one normalized message before the frame ceiling applies.
constexpr size_t HISTORY_MAX_TEXT_BYTES = 1200;

// UTF-8 safe prefix length for a byte budget, excluding a split code point.
inline size_t history_utf8_prefix(const std::string& value, size_t capacity) {
    size_t size = std::min(value.size(), capacity);
    while (size && (static_cast<unsigned char>(value[size]) & 0xc0U) == 0x80U) --size;
    return size;
}

inline std::string history_bounded_text(const std::string& value, size_t limit,
                                        bool& truncated) {
    truncated = value.size() > limit;
    return value.substr(0, history_utf8_prefix(value, limit));
}

// One scrollable view over the current page's messages. Browsing is strictly
// linear across text screens: a long message that needs several screens is
// traversed fully before the next older message is shown, and the reverse is
// exactly reversible.
//
//   message N screen 1 -> message N screen 2 -> message N screen 3 -> message N-1 screen 1
//
// screen_counts is supplied by the caller because wrapping depends on the active
// font; the model itself stays host-testable and font-free.
struct HistoryScreen {
    std::vector<std::string> blocks;   // one entry per message body
    std::vector<std::string> headers;  // USER / ASSISTANT
    std::vector<std::string> agents;   // pi / codex
    std::vector<size_t> screen_counts; // text screens needed per message
    size_t index = 0;                  // current message
    size_t page = 0;                   // current text screen of that message

    static size_t screens_for(size_t screens) { return screens ? screens : 1; }
    size_t page_count() const {
        if (blocks.empty()) return 1;
        return screens_for(index < screen_counts.size() ? screen_counts[index] : 1);
    }
    void load(const std::vector<std::string>& role_names,
              const std::vector<std::string>& agent_names,
              const std::vector<std::string>& bodies,
              const std::vector<size_t>& screens) {
        headers = role_names;
        agents = agent_names;
        blocks = bodies;
        screen_counts = screens;
        screen_counts.resize(blocks.size(), 1);
        index = 0; page = 0;
    }
    bool empty() const { return blocks.empty(); }
    size_t count() const { return blocks.size(); }
    const std::string& header() const { return headers[index]; }
    const std::string& agent() const { return agents[index]; }
    const std::string& body() const { return blocks[index]; }
    // Older = later messages, and later screens inside one message.
    bool older() {
        if (page + 1 < page_count()) { ++page; return true; }
        if (index + 1 >= blocks.size()) return false;
        ++index; page = 0;
        return true;
    }
    bool newer() {
        if (page > 0) { --page; return true; }
        if (index == 0) return false;
        --index; page = page_count() - 1;   // the newest-to-last screen of that message
        return true;
    }
    bool at_newest() const { return index == 0 && page == 0; }
    bool at_oldest() const { return blocks.empty() || (index + 1 >= blocks.size() && page + 1 >= page_count()); }
    // "2 / 4" message position, plus "2 / 3" when the message needs several
    // screens. A single-screen message keeps the compact "2 / 4" form.
    std::string position() const {
        char buffer[48];
        const size_t message = blocks.empty() ? 0 : index + 1;
        const size_t messages = blocks.empty() ? 0 : blocks.size();
        if (page_count() > 1)
            std::snprintf(buffer, sizeof(buffer), "%u / %u \xc2\xb7 %u / %u",
                          unsigned(message), unsigned(messages), unsigned(page + 1), unsigned(page_count()));
        else
            std::snprintf(buffer, sizeof(buffer), "%u / %u", unsigned(message), unsigned(messages));
        return buffer;
    }
    std::string agent_header() const {
        if (blocks.empty()) return "";
        std::string name = agents[index];
        for (char& c : name) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
        return name + " \xc2\xb7 HISTORY";
    }
};

} // namespace wearable