#pragma once

#include "camera_manager.hpp"
#include "detection_engine.hpp"
#include "game_501.hpp"

#include <array>
#include <atomic>
#include <cstdint>
#include <deque>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include <nlohmann/json.hpp>
#include <opencv2/opencv.hpp>

class Application
{
public:
    Application(int width, int height, int fps,
                const std::array<std::string, CameraManager::CameraCount> &initial_sources,
                std::string data_directory);
    ~Application();

    nlohmann::json state() const;
    nlohmann::json availableCameraSources() const;
    nlohmann::json configureCameras(const std::array<std::string, CameraManager::CameraCount> &sources);
    nlohmann::json updateCameraView(std::size_t index, const CameraManager::View &view);
    cv::Mat cameraSnapshot(std::size_t index, bool overlay) const;

    nlohmann::json startCalibration();
    nlohmann::json startDetection();
    nlohmann::json stopDetection();

    nlohmann::json startGame(const std::vector<std::string> &players);
    nlohmann::json submitDart(const std::string &score, const std::string &source = "manual");
    nlohmann::json undoDart();
    nlohmann::json markBoardClear();
    nlohmann::json resetGame();

    void shutdown();

private:
    struct CalibrationStatus
    {
        std::string state = "idle";
        std::string message = "Anslut och rikta tre kameror";
        std::int64_t started_at = 0;
        std::int64_t finished_at = 0;
    };

    static std::int64_t nowMillis();
    void processingLoop();
    void calibrationWorker();
    void invalidateCalibration(const std::string &message);
    void addEvent(const std::string &type, const std::string &message, const nlohmann::json &details = {});
    void loadConfiguration(std::array<std::string, CameraManager::CameraCount> &sources);
    void saveConfiguration() const;
    nlohmann::json calibrationStatusJson() const;

    CameraManager cameras_;
    DetectionEngine detector_;
    Game501 game_;
    std::string data_directory_;

    std::atomic<bool> shutting_down_{false};
    std::atomic<bool> processing_{false};
    std::thread processing_thread_;

    mutable std::mutex calibration_mutex_;
    CalibrationStatus calibration_status_;
    std::atomic<bool> calibration_running_{false};
    std::thread calibration_thread_;

    mutable std::mutex event_mutex_;
    std::deque<nlohmann::json> events_;
    nlohmann::json last_detection_ = nullptr;
    std::uint64_t next_event_id_ = 1;
};

