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

#include <iostream>
#include <algorithm>
#include <thread>
#include <chrono>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

// 静态变量定义
GMainLoop *RTSPManager::loop = nullptr;
GMainContext *RTSPManager::main_context = nullptr;
GstRTSPServer *RTSPManager::server = nullptr;

std::unordered_map<std::string, bool> RTSPManager::streaming_status;
std::unordered_map<std::string, GstRTSPMediaFactory *> RTSPManager::mounted_factories;
std::mutex RTSPManager::mounted_factories_mutex;

std::unordered_map<std::string, std::vector<GstRTSPMedia *>> RTSPManager::media_map;
std::mutex RTSPManager::media_map_mutex;

bool set_v4l2_format(const std::string &dev, int width, int height)
{
    int fd = open(dev.c_str(), O_RDWR);
    if (fd < 0)
    {
        LOG_ERROR("Failed to open " + dev);
        return false;
    }

    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

    // 读格式，若相同则略过
    if (ioctl(fd, VIDIOC_G_FMT, &fmt) == 0)
    {
        bool match = true;
        if (fmt.fmt.pix_mp.width != (unsigned int)width ||
            fmt.fmt.pix_mp.height != (unsigned int)height ||
            fmt.fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12)
        {
            match = false;
        }

        if (match)
        {
            close(fd);
            LOG_INFO("[RTSP] V4L2 format already NV12 " + std::to_string(width) + "x" + std::to_string(height) + " for " + dev);
            return true;
        }
    }

    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    fmt.fmt.pix_mp.width = width;
    fmt.fmt.pix_mp.height = height;
    fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
    fmt.fmt.pix_mp.num_planes = 1;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
    {
        LOG_ERROR("VIDIOC_S_FMT failed for " + dev);
        close(fd);
        return false;
    }

    LOG_INFO("[RTSP] Set V4L2 format to NV12 " + std::to_string(width) + "x" + std::to_string(height) + " for " + dev);
    close(fd);
    return true;
}

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

GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam)
{
    // 启动前把 v4l2 格式设成我们想要的
    set_v4l2_format(cam.device, cam.width, cam.height);

    int w = cam.width;
    int h = cam.height;

    std::string caps =
        "video/x-raw,format=NV12,"
        "width=" +
        std::to_string(w) +
        ",height=" + std::to_string(h) +
        ",framerate=" + std::to_string(cam.fps) + "/1";

    // 关键：tee 一路给 RTSP，一路给 fakesink，保持 v4l2src / mpph264enc 永远在跑
    //
    // 结构：
    // v4l2src -> caps -> tee name=t
    //   tee src0: t. -> queue -> mpph264enc -> h264parse -> rtph264pay (给 RTSP)
    //   tee src1: t. -> queue -> fakesink (假消费者，让 DMA 持续 dequeue)
    std::string launch_str =
        "( v4l2src device=" + cam.device +
        " io-mode=2 is-live=true do-timestamp=true"
        " ! " +
        caps +
        " ! tee name=t "
        " ! queue leaky=downstream max-size-time=0 max-size-bytes=0 max-size-buffers=0"
        " ! mpph264enc name=enc rc-mode=cbr bps=" +
        std::to_string(cam.bitrate) +
        " gop=" + std::to_string(cam.fps) +
        " header-mode=1"
        " ! h264parse"
        " ! rtph264pay name=pay0 pt=96 config-interval=1 "
        " t. ! queue leaky=downstream max-size-buffers=3 max-size-bytes=0 max-size-time=0"
        " ! fakesink sync=false async=false )";

    LOG_INFO("[RTSP] Launch for " + cam.name + ": " + launch_str);

    GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new();
    gst_rtsp_media_factory_set_launch(factory, launch_str.c_str());

    // 先保持每个客户端独立 pipeline，逻辑简单可靠
    gst_rtsp_media_factory_set_shared(factory, TRUE);

    // 客户端断开时不要乱 reset，交给我们自己处理 / 或干脆不动
    gst_rtsp_media_factory_set_suspend_mode(factory, GST_RTSP_SUSPEND_MODE_NONE);

    g_signal_connect_data(factory,
                          "media-configure",
                          G_CALLBACK(on_media_created),
                          g_strdup(cam.name.c_str()),
                          (GClosureNotify)g_free,
                          (GConnectFlags)0);

    return factory;
}

void RTSPManager::start(const std::vector<Camera> &cams)
{
    server = gst_rtsp_server_new();
    gst_rtsp_server_set_service(server, "8554");

    loop = g_main_loop_new(nullptr, FALSE);
    main_context = g_main_loop_get_context(loop);

    // 先获取 mountpoints
    GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);

    // 在这里统一挂载所有 enabled 摄像头
    for (const auto &cam : cams)
    {
        if (!cam.enabled)
            continue;

        GstRTSPMediaFactory *factory = create_media_factory(cam);
        std::string mount_point = "/" + cam.name;

        gst_rtsp_mount_points_add_factory(mounts, mount_point.c_str(), factory);

        {
            std::lock_guard<std::mutex> lock(mounted_factories_mutex);
            mounted_factories[cam.name] = factory;
            streaming_status[cam.name] = true;
        }

        LOG_INFO("[RTSP] Camera '" + cam.name +
                 "' mounted at rtsp://0.0.0.0:8554" + mount_point);
    }

    g_object_unref(mounts);

    gst_rtsp_server_attach(server, nullptr);

    LOG_INFO("[RTSP] Server running on rtsp://0.0.0.0:8554");
    g_main_loop_run(loop);

    if (server)
    {
        g_object_unref(server);
        server = nullptr;
    }
    if (loop)
    {
        g_main_loop_unref(loop);
        loop = nullptr;
    }

    LOG_INFO("[RTSP] Server stopped.");
}

void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, gpointer user_data)
{
    const char *cam_name = static_cast<const char *>(user_data);

    LOG_INFO(std::string("[RTSP] media-configure for camera: ") + cam_name);

    // 保存 media，用于之后 unmount 时统一管理
    g_object_ref(media);
    {
        std::lock_guard<std::mutex> lock(media_map_mutex);
        media_map[cam_name].push_back(media);
    }

    g_signal_connect_data(
        media,
        "unprepared",
        G_CALLBACK(on_media_unprepared),
        g_strdup(cam_name),
        (GClosureNotify)g_free,
        (GConnectFlags)0);

    // 不再手动改 pipeline 状态，交给 gst-rtsp-server 自己管理
    // 如果后面发现确实有卡在 PAUSED 的情况，再在这里加逻辑
}

void RTSPManager::on_media_unprepared(GstRTSPMedia *media, gpointer user_data)
{
    const char *cam_name = static_cast<const char *>(user_data);

    LOG_INFO(std::string("[RTSP] media-unprepared: ") + cam_name);

    {
        std::lock_guard<std::mutex> lock(media_map_mutex);
        auto it = media_map.find(cam_name);
        if (it != media_map.end())
        {
            auto &vec = it->second;
            vec.erase(std::remove(vec.begin(), vec.end(), media), vec.end());
            if (vec.empty())
                media_map.erase(it);
        }
        else
        {
            LOG_WARN(std::string("[RTSP] media-unprepared but no entry in media_map for camera: ") + cam_name);
        }
    }

    g_object_unref(media);
}

gboolean RTSPManager::mount_camera_in_main(gpointer data)
{
    Camera *cam = static_cast<Camera *>(data);
    if (!cam || !server)
    {
        delete cam;
        return G_SOURCE_REMOVE;
    }

    GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);

    std::string mount_point = "/" + cam->name;
    GstRTSPMediaFactory *factory = create_media_factory(*cam);

    gst_rtsp_mount_points_add_factory(mounts, mount_point.c_str(), factory);
    g_object_unref(mounts);

    {
        std::lock_guard<std::mutex> lock(mounted_factories_mutex);
        mounted_factories[cam->name] = factory;
        streaming_status[cam->name] = true;
    }

    LOG_INFO("[RTSP] Camera '" + cam->name + "' mounted at rtsp://localhost:8554" + mount_point);

    delete cam;
    return G_SOURCE_REMOVE;
}

gboolean RTSPManager::unmount_camera_in_main(gpointer data)
{
    Camera *cam = static_cast<Camera *>(data);
    if (!cam || !server)
    {
        delete cam;
        return G_SOURCE_REMOVE;
    }

    std::string cam_name = cam->name;
    std::string mount_point = "/" + cam_name;

    // 停掉 media
    {
        std::lock_guard<std::mutex> lock(media_map_mutex);
        auto it = media_map.find(cam_name);
        if (it != media_map.end())
        {
            for (GstRTSPMedia *media : it->second)
            {
                GstElement *pipeline = gst_rtsp_media_get_element(media);
                if (pipeline)
                {
                    gst_element_set_state(pipeline, GST_STATE_NULL);
                    gst_object_unref(pipeline);
                }

                gst_rtsp_media_unprepare(media);
            }

            it->second.clear();
            media_map.erase(it);
        }
    }

    // 移除 factory
    GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
    if (mounts)
    {
        gst_rtsp_mount_points_remove_factory(mounts, mount_point.c_str());
        g_object_unref(mounts);
    }

    {
        std::lock_guard<std::mutex> lock(mounted_factories_mutex);
        auto it = mounted_factories.find(cam_name);
        if (it != mounted_factories.end())
        {
            if (it->second)
                g_object_unref(it->second);
            mounted_factories.erase(it);
        }
        streaming_status[cam_name] = false;
    }

    LOG_INFO("[RTSP] Camera '" + cam_name + "' unmounted.");
    delete cam;
    return G_SOURCE_REMOVE;
}

void RTSPManager::mount_camera(const Camera &cam)
{
    Camera *camCopy = new Camera(cam);
    g_main_context_invoke(main_context, [](gpointer data) -> gboolean
                          { return RTSPManager::mount_camera_in_main(data); }, camCopy);
}

void RTSPManager::unmount_camera(const Camera &cam)
{
    Camera *camCopy = new Camera(cam);
    g_main_context_invoke(main_context, [](gpointer data) -> gboolean
                          { return RTSPManager::unmount_camera_in_main(data); }, camCopy);
}

bool RTSPManager::is_streaming(const std::string &cam_name)
{
    std::lock_guard<std::mutex> lock(mounted_factories_mutex);
    auto it = streaming_status.find(cam_name);
    return it != streaming_status.end() ? it->second : false;
}

bool RTSPManager::is_any_streaming()
{
    std::lock_guard<std::mutex> lock(mounted_factories_mutex);
    for (auto &kv : streaming_status)
        if (kv.second)
            return true;
    return false;
}

void RTSPManager::stop()
{
    if (loop)
    {
        g_main_context_invoke(
            main_context,
            [](gpointer data) -> gboolean
            {
                g_main_loop_quit(static_cast<GMainLoop *>(data));
                return G_SOURCE_REMOVE;
            },
            loop);
    }
}
