// Watch glance derivation: host thresholds, task mapping, Codex rendering and
// the redraw comparison that keeps the Watch off the 2 s snapshot cadence.
#include "watch_status.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <string>

using namespace wearable;

static std::string percent(const CodexQuota& q) {
    char text[16];codex_percent_text(q,text,sizeof(text));return text;
}
static std::string reset(const CodexQuota& q,const LocalClock& now) {
    char text[24];codex_reset_text(q,now,text,sizeof(text));return text;
}

int main() {
    // ---- Host: offline ------------------------------------------------
    assert(host_state(10,20,false,true)==HostState::Offline);
    assert(host_state(10,20,true,false)==HostState::Offline);
    assert(!std::strcmp(host_state_label(HostState::Offline),"Offline"));

    // ---- Host: threshold boundaries ------------------------------------
    assert(host_state(0,0,true,true)==HostState::Idle);
    assert(host_state(WATCH_ACTIVE_CPU_PERCENT-0.1f,10,true,true)==HostState::Idle);
    assert(host_state(WATCH_ACTIVE_CPU_PERCENT,10,true,true)==HostState::Active);
    assert(host_state(WATCH_BUSY_CPU_PERCENT-0.1f,10,true,true)==HostState::Active);
    assert(host_state(WATCH_BUSY_CPU_PERCENT,10,true,true)==HostState::Busy);
    assert(host_state(1,WATCH_BUSY_MEMORY_PERCENT,true,true)==HostState::Busy);
    assert(host_state(1,WATCH_BUSY_MEMORY_PERCENT-0.1f,true,true)==HostState::Idle);
    assert(host_state(100,100,true,true)==HostState::Busy);
    assert(!std::strcmp(host_state_label(HostState::Busy),"Busy"));

    // ---- Host: hysteresis keeps the row stable at the threshold ---------
    assert(host_state(71,10,true,true,HostState::Busy,true)==HostState::Busy);
    assert(host_state(69,10,true,true,HostState::Busy,true)==HostState::Busy);
    assert(host_state(64,10,true,true,HostState::Busy,true)==HostState::Active);
    assert(host_state(69,10,true,true)==HostState::Active);           // fresh: nominal
    assert(host_state(19,10,true,true,HostState::Active,true)==HostState::Active);
    assert(host_state(19,10,true,true)==HostState::Idle);
    assert(host_state(14,10,true,true,HostState::Active,true)==HostState::Idle);
    assert(host_state(69,10,true,true,HostState::Busy,false)==HostState::Active);

    // ---- Task: every authoritative state --------------------------------
    assert(!std::strcmp(task_state_label("RUNNING",true,true),"Running"));
    assert(!std::strcmp(task_state_label("WAITING_APPROVAL",true,true),"Approval"));
    assert(!std::strcmp(task_state_label("WAITING_INPUT",true,true),"Waiting"));
    assert(!std::strcmp(task_state_label("DONE",true,true),"Done"));
    assert(!std::strcmp(task_state_label("FAILED",true,true),"Failed"));
    assert(!std::strcmp(task_state_label("IDLE",true,true),"Idle"));
    assert(!std::strcmp(task_state_label("DORMANT",true,true),"Idle"));
    assert(!std::strcmp(task_state_label("OFFLINE",true,true),"Offline"));
    assert(!std::strcmp(task_state_label("RUNNING",false,true),"None"));
    assert(!std::strcmp(task_state_label(nullptr,true,true),"None"));
    assert(!std::strcmp(task_state_label("RUNNING",true,false),"Offline"));
    assert(!std::strcmp(task_state_label("SOMETHING_NEW",true,true),"Idle"));

    // Watch ignores Done elapsed and equivalent native states, but displays
    // authoritative Running timer corrections and state transitions.
    assert(!watch_task_changes_display("DONE","DONE",true,true,true,24,26));
    assert(!watch_task_changes_display("IDLE","DORMANT",true,true,true,0,99));
    assert(!watch_task_changes_display("RUNNING","RUNNING",true,true,true,12,12));
    assert(watch_task_changes_display("RUNNING","RUNNING",true,true,true,12,10));
    assert(watch_task_changes_display("RUNNING","DONE",true,true,true,12,12));
    assert(watch_task_changes_display("IDLE","IDLE",false,true,true,0,0));
    assert(!watch_task_changes_display("RUNNING","DONE",true,true,false,12,24));

    // Tones stay restrained: one accent-ish color per meaningful state.
    assert(task_state_tone("RUNNING",true,true)==TaskTone::Accent);
    assert(task_state_tone("DONE",true,true)==TaskTone::Done);
    assert(task_state_tone("FAILED",true,true)==TaskTone::Failed);
    assert(task_state_tone("WAITING_APPROVAL",true,true)==TaskTone::Approval);
    assert(task_state_tone("WAITING_INPUT",true,true)==TaskTone::Approval);
    assert(task_state_tone("IDLE",true,true)==TaskTone::Primary);
    assert(task_state_tone("RUNNING",false,true)==TaskTone::Secondary);
    assert(task_state_tone("RUNNING",true,false)==TaskTone::Secondary);

    // ---- Codex percent --------------------------------------------------
    CodexQuota quota{true,72,1759678800,true,true,300};
    assert(percent(quota)=="72%");
    assert(percent(CodexQuota{})=="--");
    CodexQuota hundred{true,100,0,false,false,0};
    assert(percent(hundred)=="100%");
    CodexQuota zero{true,0,0,false,false,0};
    assert(percent(zero)=="0%");

    // ---- Codex reset: today vs cross-day, with UTC+8 display -------------
    // 2025-10-05 23:40 local (UTC+8) = local seconds 1759707600.
    const int64_t local_2340 = 1759707600;              // local seconds
    const int64_t utc_2340 = local_2340 - 8 * 3600;     // what the gateway sends
    LocalClock today{local_2340 - 600, true};           // same local day
    CodexQuota reset_today{true,72,utc_2340,true,true,300};
    assert(reset(reset_today,today)=="23:40");
    LocalClock tomorrow{local_2340 - 90000, true};
    assert(reset(reset_today,tomorrow)=="10/05-23:40");
    // Invalid/missing reset renders nothing so the row degrades to "Codex 72%".
    assert(reset(CodexQuota{true,72,0,false,false,0},today)=="");
    assert(reset(CodexQuota{true,72,0,true,false,0},today)=="");
    assert(reset(CodexQuota{},today)=="");
    // No valid clock still yields the compact date form rather than a crash.
    assert(reset(reset_today,LocalClock{})=="10/05-23:40");
    // Same local day arithmetic must not be fooled by the UTC boundary.
    assert(same_local_day(local_2340,local_2340-60));
    assert(!same_local_day(local_2340,local_2340-86400));
    // Date math across a leap day and a year boundary (UTC+8).
    // 2028 is a leap year, so a Feb 29 reset must render as 02/29.
    const int64_t feb29_2028_noon = 1835438400;         // 2028-02-29 12:00 UTC+8
    CodexQuota leap{true,72,feb29_2028_noon - 8 * 3600,true,true,300};
    assert(reset(leap,LocalClock{feb29_2028_noon - 200000, true})=="02/29-12:00");
    // 2026-01-01 09:00 local must not roll back to 2025.
    const int64_t jan01_2026 = 1767225600;              // 2026-01-01 00:00 UTC+8
    CodexQuota newyear{true,72,jan01_2026 - 8 * 3600,true,true,300};
    assert(reset(newyear,LocalClock{jan01_2026 - 200000, true})=="01/01-00:00");

    // ---- Redraw policy: identical derived display must not repaint ------
    const char* text_a="23:40";const char* text_b="23:40";
    assert(!codex_quota_changes_display(quota,quota,text_a,text_b));
    // Same raw percent, different reset rendering -> repaint.
    assert(codex_quota_changes_display(quota,quota,"23:40","10/08-08:00"));
    // Percent change -> repaint.
    CodexQuota lower{true,71,utc_2340,true,true,300};
    assert(codex_quota_changes_display(quota,lower,text_a,text_b));
    // Validity change -> repaint.
    assert(codex_quota_changes_display(quota,CodexQuota{},text_a,""));
    assert(codex_quota_changes_display(CodexQuota{},quota,"",text_a));
    // Unavailable on both sides -> nothing to repaint.
    assert(!codex_quota_changes_display(CodexQuota{},CodexQuota{},"",""));

    std::puts("Watch glance host thresholds/hysteresis, task mapping, Codex percent/reset, redraw policy PASS");
    return 0;
}