#pragma once
#include <cstdint>
#include <cstddef>
#include <string>
#include <vector>

// Bounded, host-testable model for the Agent manual browse and the native
// History view. The device never keeps a second conversation database: it holds
// at most kCachePages pages of normalized user/assistant messages plus the
// opaque cursor that fetches the next older page.
namespace wearable {

// Only these two roles are ever shown. Reasoning, commentary, tool output,
// command output, file changes and internal metadata never enter this model.
enum class HistoryRole : uint8_t { User, Assistant };

struct HistoryMessage {
    HistoryRole role = HistoryRole::User;
    std::string agent;   // "pi" / "codex", for the header only
    std::string text;    // bounded, UTF-8 safe
    bool truncated = false;
};

struct HistoryPage {
    std::vector<HistoryMessage> messages;  // newest first within the page
    // Opaque cursor for the next OLDER page; empty means oldest reached.
    std::string next_cursor;
    std::string previous_cursor;
    uint32_t generation = 0;
    bool valid = false;
    unsigned fragment = 0, fragments = 0;
};

// Device cache: current page plus a small bounded neighbourhood, never the whole
// session. Oldest pages are evicted first so RAM stays fixed.
class AgentHistoryCache {
public:
    static constexpr size_t kCachePages = 3;   // fixed upper bound
    static constexpr size_t kMaxMessages = 4;  // per page

    void clear() { pages_.clear(); index_ = 0; }
    bool empty() const { return pages_.empty(); }
    size_t size() const { return pages_.size(); }
    size_t index() const { return index_; }

    // Replace the cache with a freshly fetched newest page.
    void reset(const HistoryPage& page) {
        pages_.clear();
        pages_.push_back(page);
        index_ = 0;
    }

    // Append an older page fetched through next_cursor and make it the current
    // page: "older" means the user asked to move further back in time, so the
    // freshly fetched page must become the displayed one. Cache content and the
    // displayed page are the same state; they are never updated independently.
    //
    // The window is a hard RAM bound, so once it is full the newest end is
    // evicted: the just-fetched page is the one that must be shown, and the
    // newest page cannot also be retained. Newer walks the retained window,
    // then uses previous_cursor to refetch evicted fragments.
    bool append_older(const HistoryPage& page) {
        if (!page.valid || pages_.empty()) return false;
        pages_.push_back(page);
        index_ = pages_.size() - 1;   // the newly fetched older page is current
        while (pages_.size() > kCachePages) { pages_.erase(pages_.begin()); --index_; }
        return true;
    }

    bool prepend_newer(const HistoryPage& page) {
        if (!page.valid || pages_.empty()) return false;
        pages_.insert(pages_.begin(),page); index_=0;
        while(pages_.size()>kCachePages) pages_.pop_back();
        return true;
    }

    // Move one page older / newer inside the cache. Returns false when the cache
    // boundary is reached (the caller then fetches or leaves History).
    bool older() { if (index_ + 1 >= pages_.size()) return false; ++index_; return true; }
    bool newer() { if (index_ == 0) return false; --index_; return true; }
    bool at_oldest() const { return pages_.empty() || index_ + 1 >= pages_.size(); }
    bool at_newest() const { return index_ == 0; }
    const HistoryPage& current() const { return pages_[index_]; }
    const std::string& older_cursor() const { return current().next_cursor; }

private:
    std::vector<HistoryPage> pages_;
    size_t index_ = 0;
};

// Manual browse state for the live Agent page. Live/auto tail is the default;
// the first vertical swipe enters Manual Browse and a return to newest resumes
// Live Tail. A target change resets the manual page.
struct AgentBrowse {
    bool manual = false;      // user controls the local page
    size_t page = 0;          // 0 == newest local page
    size_t page_count = 1;    // local pages available in the current tail

    void reset() { manual = false; page = 0; page_count = 1; }
    void set_pages(size_t count) { page_count = count ? count : 1; if (page >= page_count) page = page_count - 1; }
    bool live_tail() const { return !manual; }
    // Older = further back in time. At the oldest local page the caller may
    // continue into History.
    bool older() {
        if (page + 1 >= page_count) return false;
        ++page; manual = true; return true;
    }
    bool newer() {
        if (page == 0) { manual = false; return false; }
        --page;
        if (page == 0) manual = false;   // back to newest resumes Live Tail
        return true;
    }
    bool at_oldest_local() const { return page + 1 >= page_count; }
};

// History view mode is a child of the SETTINGS modal so it reuses the same
// modal ownership, epoch and stale protection as every other overlay.
enum class HistoryView : uint8_t { Closed, Browser, Current };

} // namespace wearable
