// rtsp_manager.cpp
#include "rtmp_manager.hpp"

#include <chrono>
#include <iostream>
#include <thread>

#include "logger.hpp"

static inline std::string stream_type_suffix(StreamType type) { return (type == StreamType::MAIN) ? "_main" : "_sub"; }

std::unordered_map<std::string, std::unique_ptr<RTMPManager::StreamContext>> RTMPManager::streams;
std::mutex RTMPManager::streams_mutex;
RTMPManager::StreamCallback RTMPManager::status_callback = nullptr;

std::string RTMPManager::make_stream_key(const std::string &cam_name, StreamType type)
{
    return cam_name + stream_type_suffix(type);
}

void RTMPManager::init()
{
    gst_init(nullptr, nullptr);
    LOG_INFO("[RTMP] GStreamer initialized.");
}

void RTMPManager::set_status_callback(StreamCallback cb) { status_callback = std::move(cb); }

void RTMPManager::update_status(const std::string &key, const StreamStatus &status)
{
    {
        std::lock_guard<std::mutex> lock(streams_mutex);
        auto it = streams.find(key);
        if (it != streams.end()) it->second->status = status;
    }
    if (status_callback) status_callback(key, status);
}

GstElement *RTMPManager::create_pipeline(const Camera &cam, StreamType type)
{
    int width = cam.width, height = cam.height, fps = cam.fps, bitrate = cam.bitrate;
    if (type == StreamType::SUB)
    {
        width = std::max(160, width / 2);
        height = std::max(120, height / 2);
        fps = std::max(10, fps / 2);
        bitrate = std::max(300000, bitrate / 2);
    }

    std::string stream_name = cam.name + stream_type_suffix(type);
    std::string pipeline_str = "v4l2src device=" + cam.device +
                               " ! video/x-raw,format=NV12,width=" + std::to_string(width) +
                               ",height=" + std::to_string(height) + ",framerate=" + std::to_string(fps) +
                               "/1 ! queue max-size-buffers=1 leaky=downstream "
                               "! mpph264enc bps=" +
                               std::to_string(bitrate) + " gop=" + std::to_string(fps) +
                               " ! h264parse ! flvmux streamable=true name=mux "
                               "! rtmpsink location=\"rtmp://127.0.0.1/live/" +
                               stream_name + " live=1\" sync=false";

    LOG_INFO("[RTMP] Creating pipeline for '" + stream_name + "': " + pipeline_str);

    GError *error = nullptr;
    GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), &error);
    if (error)
    {
        LOG_ERROR("[RTMP] Pipeline parse error: " + std::string(error->message));
        g_error_free(error);
        return nullptr;
    }
    return pipeline;
}

int get_camera_index(const std::string &name)
{
    for (size_t i = 0; i < g_app_config.cameras.size(); ++i)
        if (g_app_config.cameras[i].name == name) return static_cast<int>(i);
    return -1;
}

RTMPManager::StreamResultInfo RTMPManager::start_camera(const Camera &cam, StreamType type)
{
    StreamResultInfo res;
    res.loc = get_camera_index(cam.name);
    res.url = get_stream_url(cam.name, type);

    std::string key = make_stream_key(cam.name, type);
    {
        std::lock_guard<std::mutex> lock(streams_mutex);
        auto it = streams.find(key);
        if (it != streams.end() && it->second->running.load())
        {
            res.result = 0;
            res.reason = "Already streaming";
            return res;
        }

        auto ctx = std::make_unique<StreamContext>();
        ctx->running.store(true);
        ctx->thread = std::thread([cam, type]() { stream_loop(cam, type); });
        streams.emplace(key, std::move(ctx));
    }

    res.result = 0;
    res.reason = "Started OK";
    return res;
}

RTMPManager::StreamResultInfo RTMPManager::stop_camera(const std::string &cam_name, StreamType type)
{
    StreamResultInfo res;
    res.loc = get_camera_index(cam_name);
    res.url = get_stream_url(cam_name, type);

    std::unique_ptr<StreamContext> ctx;
    std::string key = make_stream_key(cam_name, type);
    {
        std::lock_guard<std::mutex> lock(streams_mutex);
        auto it = streams.find(key);
        if (it == streams.end())
        {
            res.result = 0;
            res.reason = "Not streaming";
            return res;
        }

        it->second->running.store(false);
        ctx = std::move(it->second);
        streams.erase(it);
    }

    if (ctx->thread.joinable()) ctx->thread.join();

    res.result = 0;
    res.reason = "Stopped manually";
    return res;
}

void RTMPManager::stop_all()
{
    std::vector<std::unique_ptr<StreamContext>> ctxs;
    {
        std::lock_guard<std::mutex> lock(streams_mutex);
        for (auto &kv : streams) kv.second->running.store(false);
        for (auto &kv : streams) ctxs.push_back(std::move(kv.second));
        streams.clear();
    }

    for (auto &ctx : ctxs)
        if (ctx->thread.joinable()) ctx->thread.join();

    LOG_INFO("[RTMP] stop_all completed.");
}

bool RTMPManager::is_streaming(const std::string &cam_name, StreamType type)
{
    std::lock_guard<std::mutex> lock(streams_mutex);
    auto it = streams.find(make_stream_key(cam_name, type));
    return it != streams.end() && it->second->running.load();
}

bool RTMPManager::is_any_streaming()
{
    std::lock_guard<std::mutex> lock(streams_mutex);
    for (auto &kv : streams)
        if (kv.second->running.load()) return true;
    return false;
}

std::string RTMPManager::get_stream_url(const std::string &cam_name, StreamType type)
{
    return "rtmp://127.0.0.1/live/" + make_stream_key(cam_name, type);
}

void RTMPManager::stream_loop(Camera cam, StreamType type)
{
    std::string key = make_stream_key(cam.name, type);
    LOG_INFO("[RTMP] Stream loop started for " + key);

    int retries = 0;

    while (true)
    {
        {
            std::lock_guard<std::mutex> lock(streams_mutex);
            auto it = streams.find(key);
            if (it == streams.end() || !it->second->running.load()) break;
        }

        GstElement *pipeline = create_pipeline(cam, type);
        if (!pipeline)
        {
            update_status(key, {false, StreamResult::PIPELINE_ERROR, "Failed to create pipeline"});
            if (++retries <= MAX_RETRIES)
            {
                LOG_WARN("[RTMP] Retrying pipeline creation for " + key);
                std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_DELAY_MS));
                continue;
            }
            break;
        }

        GstBus *bus = gst_element_get_bus(pipeline);
        gst_element_set_state(pipeline, GST_STATE_PLAYING);

        bool first_frame_received = false;
        bool stop_flag = false;
        auto first_frame_start = std::chrono::steady_clock::now();

        while (!stop_flag)
        {
            GstMessage *msg = gst_bus_timed_pop_filtered(
                bus, 100 * GST_MSECOND,
                static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED));

            {
                std::lock_guard<std::mutex> lock(streams_mutex);
                auto it = streams.find(key);
                if (it == streams.end() || !it->second->running.load())
                {
                    stop_flag = true;
                    break;
                }
            }

            if (!first_frame_received)
            {
                auto elapsed = std::chrono::steady_clock::now() - first_frame_start;
                if (std::chrono::duration_cast<std::chrono::seconds>(elapsed).count() > 5)
                {
                    update_status(key, {false, StreamResult::TIMEOUT, "No frames received"});
                    stop_flag = true;
                }
            }

            if (!msg) continue;

            switch (GST_MESSAGE_TYPE(msg))
            {
                case GST_MESSAGE_ERROR:
                {
                    GError *err = nullptr;
                    gst_message_parse_error(msg, &err, nullptr);
                    update_status(key, {false, StreamResult::CONNECTION_FAIL, err ? err->message : "Unknown error"});
                    if (err) g_error_free(err);
                    stop_flag = true;
                    break;
                }
                case GST_MESSAGE_EOS:
                    update_status(key, {false, StreamResult::EOS_RECEIVED, "EOS"});
                    stop_flag = true;
                    break;
                case GST_MESSAGE_STATE_CHANGED:
                {
                    GstState old_state, new_state;
                    gst_message_parse_state_changed(msg, &old_state, &new_state, nullptr);
                    if (GST_MESSAGE_SRC(msg) == GST_OBJECT(pipeline) && new_state == GST_STATE_PLAYING &&
                        !first_frame_received)
                    {
                        first_frame_received = true;
                        update_status(key, {true, StreamResult::OK, ""});
                        retries = 0;  // 成功后重置重试计数
                    }
                    break;
                }
                default:
                    break;
            }

            gst_message_unref(msg);
        }

        gst_element_set_state(pipeline, GST_STATE_NULL);
        if (bus) gst_object_unref(bus);
        gst_object_unref(pipeline);

        // 自动重试逻辑
        if (stop_flag && retries < MAX_RETRIES)
        {
            retries++;
            LOG_WARN("[RTMP] Stream " + key + " failed, retrying... (" + std::to_string(retries) + ")");
            std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_DELAY_MS));
            continue;
        }

        break;
    }

    update_status(key, {false, StreamResult::UNKNOWN, "Stream loop exited"});
    LOG_INFO("[RTMP] Stream loop ended for " + key);
}

std::vector<RTMPManager::StreamResultInfo> RTMPManager::process_push_request(const VideoPushRequest &req)
{
    std::vector<StreamResultInfo> results;

    for (const auto &item : req.data)
    {
        StreamType type = (item.streamType == 0) ? StreamType::MAIN : StreamType::SUB;

        for (int ch : item.channels)
        {
            if (ch < 0 || ch >= static_cast<int>(g_app_config.cameras.size())) continue;

            const auto &cam = g_app_config.cameras[ch];
            StreamResultInfo info;

            if (item.switchVal == 0)
            {
                // 开启推流
                info = start_camera(cam, type);
            }
            else
            {
                // 停止推流
                info = stop_camera(cam.name, type);
            }

            results.push_back(info);
        }
    }

    return results;
}
