#include "generic_barrier.h"

#include "application.h"
#include "recognize_processor.h"
#include "common/date_util.h"
#include "common/logging.h"
#include "common/snowflake.h"
#include "common/sys_util.h"

namespace zsy
{
    GenericBarrier::GenericBarrier(const BarrierProperties &config)
        : config(config)
    {
        Application::eventManager->subscribe(config.sn + "/open", [this](const EventManager::Event &)
        {
            this->open();
        });
        static auto subscribed = []
        {
            auto cli = Application::mqttCliHolder->remoteCli();
            if (cli)
            {
                auto clientId = cli->getClientId();
                Application::eventManager->subscribe(clientId + "/" + clientId + "/1/barrier", [](const EventManager::Event &event)
                {
                    if (!event.data.has_value())
                    {
                        LOGGER_ERROR("开门请求参数错误");
                        return;
                    }
                    auto jsonData = event.data.value();

                    LOGGER_INFO("收到开门请求：{}", jsonData.dump());
                    const auto req = jsonData.get<DoorReq>();
                    Application::eventManager->publish(req.deviceNo + "/open", jsonData.dump());
                });
            } else
            {
                LOGGER_ERROR("未找到 MQTT 远程客户端");
            }

            Application::httpSvr->postEndpoint("/plateRecognize", [](const httplib::Request &req, httplib::Response &res)
            {
                try
                {
                    auto body = req.body;
                    LOGGER_INFO("收到车牌识别结果：{}", body);

                    auto [succ,recognizeResult,name,sn] = GenericBarrier::parseRecognizeResult(body);
                    if (succ)
                    {
                        std::jthread t([recognizeResult,name,sn]
                        {
                            LOGGER_INFO("正在处理车牌识别结果：{}、{}、{}", recognizeResult->license, name, sn);
                            {
                                std::lock_guard<std::mutex> lock(tasks_mutex);
                                if (tasks.contains(sn))
                                {
                                    if (auto oldLicense = tasks.at(sn); oldLicense == recognizeResult->license)
                                    {
                                        LOGGER_INFO("重复识别：{}、{}", sn, oldLicense);
                                    } else
                                    {
                                        Application::deviceHolder->getSoundColumn(sn)->play("请等待前车业务完成后重新识别");
                                        LOGGER_INFO("当前道闸正在处理：{}、{}", sn, oldLicense);
                                    }
                                    return;
                                } else
                                {
                                    tasks[sn] = recognizeResult->license;
                                }
                            }
                            RecognizeProcessor::process(recognizeResult);
                            {
                                std::lock_guard<std::mutex> lock(tasks_mutex);
                                tasks.erase(sn);
                            }
                            LOGGER_INFO("车牌识别结果处理完成");
                        });
                        t.detach();
                    }
                } catch (std::exception &e)
                {
                    LOGGER_ERROR("车牌识别结果处理失败: {}", e.what());
                } catch (...)
                {
                    LOGGER_ERROR("未知异常，车牌识别结果处理失败");
                }
                res.set_content(R"({})", "application/json");
            });
            return true;
        }();
    }

    void GenericBarrier::open()
    {
        LOGGER_INFO("收到开门请求：{} {}", config.name, config.sn);
        const long timestamp = static_cast<long>(DateUtil::timestamp() / 1000);
        BarrierBcd barrier;
        barrier.id = "open" + std::to_string(timestamp);
        barrier.sn = config.sn;
        barrier.timestamp = timestamp;
        barrier.payload.body.value = 2;
        barrier.payload.body.io = config.io;
        nlohmann::json j(barrier);


        auto topic = "device/" + config.sn + "/message/down/gpio_out";
        auto data = j.dump();

        LOGGER_INFO("打开道闸：{} {}", topic, data);

        try
        {
            auto cli = Application::mqttCliHolder->localCli();
            if (cli)
            {
                cli->publish(topic, data, 0);
            } else
            {
                LOGGER_ERROR("未找到 MQTT 本地客户端");
            }
        } catch (const std::exception &e)
        {
            LOGGER_INFO("开门失败:{}", e.what());
        } catch (...)
        {
            LOGGER_ERROR("未知异常，开门失败");
        }
    }

    bool GenericBarrier::isFront()
    {
        return config.name.find("前置") != std::string::npos;
    }

    std::string GenericBarrier::sn()
    {
        return config.sn;
    }

    std::string GenericBarrier::name()
    {
        return config.name;
    }

    std::shared_ptr<std::istream> GenericBarrier::resolvePhoto(const std::string &imageFile)
    {
        auto content = SysUtil::base64_decode(imageFile);
        return std::make_shared<std::istringstream>(content, std::ios_base::in | std::ios_base::binary);
    }

    std::tuple<bool, std::shared_ptr<RecognizeData>, std::string, std::string> GenericBarrier::parseRecognizeResult(const std::string &jsonStr)
    {
        try
        {
            auto json = nlohmann::json::parse(jsonStr);
            RecognizeResult result = json.get<RecognizeResult>();
            auto sn = result.alarmInfoPlate.serialno;
            auto barrier = Application::deviceHolder->getBarrier(sn);
            if (!barrier)
            {
                LOGGER_ERROR("未找到对应的道闸");
                return std::make_tuple(false, nullptr, "", "");
            }
            auto image_file = result.alarmInfoPlate.result.plateResult.imageFile;
            auto license = result.alarmInfoPlate.result.plateResult.license;

            if (sn.empty()
                || image_file.empty()
                || license.empty()
                || license.find("无") != std::string::npos
            )
            {
                LOGGER_ERROR("车牌识别结果解析失败或车牌为：“无”");
                return std::make_tuple(false, nullptr, "", "");
            }
            return std::make_tuple(true, std::make_shared<RecognizeData>(
                                       barrier->isFront(),
                                       sn,
                                       license,
                                       barrier->resolvePhoto(image_file)
                                   ),
                                   barrier->name(), barrier->sn());
        } catch (std::exception &e)
        {
            LOGGER_ERROR("未找到车牌识别结果：{}", e.what());
            return std::make_tuple(false, nullptr, "", "");
        } catch (...)
        {
            LOGGER_ERROR("未知异常，未找到车牌识别结果");
            return std::make_tuple(false, nullptr, "", "");
        }
    }
} // zsy
