#pragma once
#include "types.hpp"
#include "config.hpp"

#include <map>
#include <iostream>

// ── SceneTrackerFunc ──────────────────────────────────────────────────────────
// KPN node: maintains an extinction-timer state machine per identified actor.
//
// On each MatchedSceneFrame:
//   1. Update last_seen for every matched known actor.
//   2. Expire actors whose last_seen is older than extinction_sec.
//   3. Emit SceneAnnotation with all currently active (non-expired) actors,
//      including their most recently seen bbox and best similarity score.
//
// Unknown faces (actor_idx == -1) are passed through per-frame but are NOT
// tracked across frames — each frame reports its own unknowns independently.

struct SceneTrackerFunc {
    static constexpr std::string_view label() { return "scene_tracker"; }

    explicit SceneTrackerFunc(const Config& cfg)
        : extinction_sec_(cfg.extinction_sec)
    {
        std::cerr << "[scene_tracker] extinction_sec=" << extinction_sec_ << "\n";
    }

    // Runtime setter for pipeline reuse across a sweep. Also clears the active-actor
    // state so a re-run starts clean (no carry-over from the previous config's film).
    void set_extinction_sec(double s) { extinction_sec_ = s; active_.clear(); }

    SceneAnnotation operator()(MatchedSceneFrame mf) {
        if (mf.source.eof) return {0.0, {}, /*eof=*/true};

        double now = mf.source.timestamp_sec;

        // Update known actors
        for (const auto& ia : mf.actors) {
            if (ia.actor_idx < 0) continue;  // skip unknowns

            auto& slot = active_[ia.actor_idx];
            slot.last_seen   = now;
            slot.last_bbox   = ia.bbox;
            slot.last_crop   = ia.crop;
            slot.name        = ia.name;
            slot.imdb_id     = ia.imdb_id;
            slot.tmdb_id     = ia.tmdb_id;
            slot.jellyfin_id = ia.jellyfin_id;
            // Keep the best (highest) similarity seen in this window
            if (ia.similarity > slot.best_similarity)
                slot.best_similarity = ia.similarity;
        }

        // Expire stale actors
        for (auto it = active_.begin(); it != active_.end(); ) {
            if ((now - it->second.last_seen) > extinction_sec_)
                it = active_.erase(it);
            else
                ++it;
        }

        // Build annotation: active known actors
        std::vector<IdentifiedActor> visible;
        visible.reserve(active_.size() + mf.actors.size());

        for (const auto& [actor_idx, slot] : active_) {
            IdentifiedActor ia;
            ia.actor_idx   = actor_idx;
            ia.name        = slot.name;
            ia.imdb_id     = slot.imdb_id;
            ia.tmdb_id     = slot.tmdb_id;
            ia.jellyfin_id = slot.jellyfin_id;
            ia.similarity = slot.best_similarity;
            ia.bbox       = slot.last_bbox;
            ia.crop       = slot.last_crop;
            visible.push_back(ia);
        }

        // Append per-frame unknowns (actor_idx == -1) directly
        for (const auto& ia : mf.actors) {
            if (ia.actor_idx < 0) visible.push_back(ia);
        }

        return {now, std::move(visible)};
    }

private:
    struct Slot {
        double      last_seen{0.0};
        float       best_similarity{0.f};
        cv::Rect2f  last_bbox;
        cv::Mat     last_crop;
        std::string name;
        std::string imdb_id;
        std::string tmdb_id;
        std::string jellyfin_id;
    };

    double               extinction_sec_;
    std::map<int, Slot>  active_;   // actor_idx → state
};
