#pragma once
#include <algorithm>
#include <cstddef>
#include <cstdint>

// Metadata only; captured before JSON parsing. No payload copies or allocation.
struct SpeechRxFrame {
    uint32_t identity = 0, parts = 0;
    size_t wire_bytes = 0;
    int64_t first_us = 0, complete_us = 0, max_part_gap_us = 0;
};

struct SpeechFragmentClock {
    SpeechRxFrame frame;
    int64_t previous_us = 0;
    void part(bool first, int64_t now) {
        if (first) {
            const auto identity = frame.identity + 1;
            frame = {};
            frame.identity = identity;
            frame.first_us = now;
        } else if (frame.parts) {
            frame.max_part_gap_us = std::max(frame.max_part_gap_us, now - previous_us);
        }
        ++frame.parts;
        previous_us = now;
    }
};

// Protected by the existing speech mutex, never held during codec writes.
struct SpeechPlaybackMetrics {
    uint32_t generation = 0;
    unsigned accepted = 0, played = 0, rejected = 0, discarded = 0, high_water = 0;
    unsigned write_calls = 0, slow_writes = 0, write_errors = 0, in_flight = 0;
    uint64_t bytes_accepted = 0, bytes_written = 0, write_us = 0, max_write_us = 0;
    int64_t start_us = 0, first_write_us = 0, last_write_us = 0;
    bool closed = false;
    void accept(size_t bytes, unsigned depth) {
        if (bytes) { ++accepted; bytes_accepted += bytes; }
        high_water = std::max(high_water, depth);
    }
    void writes(unsigned calls, uint64_t bytes, uint64_t total_us, uint64_t maximum_us,
                unsigned slow, unsigned errors, int64_t first, int64_t last, bool full_frame) {
        write_calls += calls; bytes_written += bytes; write_us += total_us;
        max_write_us = std::max(max_write_us, maximum_us);
        slow_writes += slow; write_errors += errors;
        if (calls && !first_write_us) first_write_us = first;
        if (calls) last_write_us = last;
        played += full_frame;
    }
};
