#pragma once

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <vector>
#include "tensor_contract.hpp"

namespace contextra {

struct DetectionPolicy {
    bool useConfidenceFloor = false;
    float confidenceFloor = 0.0f;
};

struct Detection {
    float score;
    int classId;
    float left, top, width, height;
};

inline float sigmoid(float value)
{
    if (value >= 0.0f) return 1.0f / (1.0f + std::exp(-value));
    const float e = std::exp(value);
    return e / (1.0f + e);
}

inline std::vector<Detection> decodeDetections(
    const float* boxes, const float* logits, uint32_t queries,
    uint32_t width, uint32_t height, const DetectionPolicy& policy, bool fire = false)
{
    std::vector<Detection> result;
    if (!boxes || !logits || !queries || !width || !height) return result;
    struct Candidate { float score; size_t index; };
    std::vector<Candidate> candidates;
    const uint32_t classCount = fire ? 3 : kModelClassCount;
    const size_t count = static_cast<size_t>(queries) * classCount;
    candidates.reserve(count);
    for (size_t i = 0; i < count; ++i) {
        // 비정상 logit은 순위 비교에 참여시키지 않는다.
        if (std::isfinite(logits[i])) candidates.push_back({sigmoid(logits[i]), i});
    }
    // MODELCONTRACT-01: 전체 query/class 상위 300개 선택 후 mapping과 정책을 적용한다.
    const size_t selected = std::min<size_t>(300, candidates.size());
    std::partial_sort(candidates.begin(), candidates.begin() + selected, candidates.end(),
        [](const Candidate& a, const Candidate& b) {
            return a.score != b.score ? a.score > b.score : a.index < b.index;
        });
    result.reserve(selected);
    for (size_t i = 0; i < selected; ++i) {
        const Candidate& c = candidates[i];
        const uint32_t slot = c.index % classCount;
        // FIREPATH-01: Fire slots 0/1 are smoke/flame, never COCO person/bicycle.
        const int classId = fire ? (slot < 2 ? static_cast<int>(slot) : -1) : canonicalClassId(slot);
        if (classId < 0 || (policy.useConfidenceFloor && c.score <= policy.confidenceFloor)) continue;
        const float* b = boxes + (c.index / classCount) * kBoxWidth;
        if (!std::isfinite(b[0]) || !std::isfinite(b[1]) ||
            !std::isfinite(b[2]) || !std::isfinite(b[3]) || b[2] < 0 || b[3] < 0) continue;
        const auto clip = [](double v, double limit) {
            return static_cast<float>(std::max(0.0, std::min(v, limit)));
        };
        // cxcywh 자체를 자르지 않고 pixel XYXY 변환 뒤 영상 경계로 제한한다.
        const float left = clip((double(b[0]) - double(b[2]) / 2) * width, width);
        const float top = clip((double(b[1]) - double(b[3]) / 2) * height, height);
        const float right = clip((double(b[0]) + double(b[2]) / 2) * width, width);
        const float bottom = clip((double(b[1]) + double(b[3]) / 2) * height, height);
        const float w = right - left, h = bottom - top;
        if (w <= 0 || h <= 0) continue;
        result.push_back({c.score, classId, left, top, w, h});
    }
    return result;
}
}  // namespace contextra
