#include <mission_adapters/event_processor.h>

#include <utility>

#include <robot/robot.h>

namespace mission_adapters
{
    // ─────────────────────────────────────────────────────────────────────────
    // EventProcessor
    // ─────────────────────────────────────────────────────────────────────────

    EventProcessor::EventProcessor(MissionManager& mission_manager, PluginRegistry& registry)
        : mission_manager_(mission_manager)
        , registry_(registry)
    {}

    EventProcessor::~EventProcessor() { stop(); }

    void EventProcessor::start()
    {
        if (running_) return;
        running_ = true;
        event_bus_.reset();
        worker_ = std::thread(&EventProcessor::spin, this);
    }

    void EventProcessor::stop()
    {
        running_ = false;
        event_bus_.stop();
        if (worker_.joinable()) worker_.join();
    }

    void EventProcessor::spin()
    {
        Event event;
        while (running_)
        {
            // Emergency được xử lý trước mọi thứ khác, kể cả khi hàng đợi đang dài.
            handlePendingEmergency();

            if (!event_bus_.pop(event))
                continue;

            // Emergency có thể vừa tới trong lúc đang chờ pop(): xử lý nó trước sự kiện vừa lấy ra.
            handlePendingEmergency();

            process(event);
        }
    }

    void EventProcessor::handlePendingEmergency()
    {
        if (event_bus_.takeEmergency())
            mission_manager_.emergency();
    }

    void EventProcessor::handleRequest(const MissionRequest& request)
    {
        MissionSourceAdapter* adapter = registry_.find(request.schema);
        if (!adapter)
        {
            robot::log_error("EventProcessor: no source declares schema '%s'",
                             request.schema.c_str());
            return;
        }

        std::string reason;
        if (!adapter->validate(request, reason))
        {
            // A1: payload hỏng bị chặn tại đây, hàng đợi đang chạy không bị đụng tới.
            robot::log_warning("EventProcessor: rejecting request schema '%s' — %s",
                               request.schema.c_str(), reason.c_str());
            return;
        }

        const ConversionResult result = adapter->convert(request);

        // Bất biến D8, kiểm ở core chứ không chỉ trong adapter: một plugin bên thứ ba sinh ra
        // mission không goal và cũng không action sẽ khiến navigation nhận một chặng không có việc
        // gì để làm, và chặng đó không bao giờ báo kết quả về.
        for (const auto& mission : result.missions)
        {
            if (!mission)
            {
                robot::log_error("EventProcessor: adapter for schema '%s' returned a null mission "
                                 "— dropping the whole batch",
                                 request.schema.c_str());
                return;
            }

            if (!mission->has_goal && mission->actions.empty())
            {
                robot::log_error("EventProcessor: adapter for schema '%s' produced a mission with "
                                 "neither goal nor action — dropping the whole batch",
                                 request.schema.c_str());
                return;
            }
        }

        if (result.empty())
        {
            robot::log_warning("EventProcessor: request schema '%s' produced no mission — the "
                               "queue is left untouched", request.schema.c_str());
            return;
        }

        if (result.mode == SubmitMode::kAppend)
            mission_manager_.append(result.missions);
        else
            mission_manager_.submit(result.missions);
    }

    void EventProcessor::process(const Event& event)
    {
        switch (event.type)
        {
            case EventType::SUBMIT_REQUEST:  handleRequest(event.request);                 break;
            case EventType::NAV_DONE:        mission_manager_.onNavigationDone(event.mission_id);   break;
            case EventType::NAV_FAILED:      mission_manager_.onNavigationFailed(event.mission_id); break;
            case EventType::PAUSE:           mission_manager_.pause();                     break;
            case EventType::RESUME:          mission_manager_.resume();                    break;
            case EventType::CANCEL:          mission_manager_.cancel();                    break;

            // Đã được xử lý ngay lúc phát qua cờ out-of-band; lần gọi này là idempotent và chỉ giữ
            // cho dòng thời gian trong log liền mạch.
            case EventType::EMERGENCY:       mission_manager_.emergency();                 break;

            case EventType::CLEAR_EMERGENCY: mission_manager_.clearEmergency();            break;
            default:
                robot::log_error("EventProcessor: event of unknown type");
                break;
        }
    }

    void EventProcessor::submitRequest(const MissionRequest& request)
    {
        if (request.schema.empty())
        {
            robot::log_error("EventProcessor: request declares no schema");
            return;
        }

        Event event;
        event.type    = EventType::SUBMIT_REQUEST;
        event.request = request;
        event_bus_.push(std::move(event));
    }

    void EventProcessor::orderEvent(const robot_protocol_msgs::Order& order)
    {
        submitRequest(MissionRequest::fromOrder(order));
    }

    void EventProcessor::goalEvent(const robot_geometry_msgs::PoseStamped& goal)
    {
        submitRequest(MissionRequest::fromPose(goal));
    }

    void EventProcessor::pauseEvent()
    {
        Event event;
        event.type = EventType::PAUSE;
        event_bus_.push(std::move(event));
    }

    void EventProcessor::resumeEvent()
    {
        Event event;
        event.type = EventType::RESUME;
        event_bus_.push(std::move(event));
    }

    void EventProcessor::cancelEvent()
    {
        Event event;
        event.type = EventType::CANCEL;
        event_bus_.push(std::move(event));
    }

    void EventProcessor::navDoneEvent(MissionId id)
    {
        Event event;
        event.type = EventType::NAV_DONE;
        event.mission_id = id;
        event_bus_.push(std::move(event));
    }

    void EventProcessor::navFailedEvent(MissionId id)
    {
        Event event;
        event.type = EventType::NAV_FAILED;
        event.mission_id = id;
        event_bus_.push(std::move(event));
    }

    void EventProcessor::emergencyEvent()
    {
        Event event;
        event.type = EventType::EMERGENCY;
        event_bus_.pushEmergency(std::move(event));
    }

    void EventProcessor::clearEmergencyEvent()
    {
        // Không đi đường out-of-band: gỡ emergency là hành động có chủ đích của người vận hành,
        // phải xếp sau mọi thứ đã phát trước nó.
        Event event;
        event.type = EventType::CLEAR_EMERGENCY;
        event_bus_.push(std::move(event));
    }

} // namespace mission_adapters
