#include "generic_barrier.h"

#include "application.h"
#include "common/loging.h"
#include "common/snowflake.h"
#include "common/sys_util.h"

namespace zsy
{
    static long long getTime()
    {
        const auto now = std::chrono::system_clock::now();
        const auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
        const auto epoch = now_ms.time_since_epoch();
        return std::chrono::duration_cast<std::chrono::milliseconds>(epoch).count();
    }

    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 + "/yztq/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->getEndpoint("/data", [](const httplib::Request &req, httplib::Response &res)
            {
                nlohmann::json j;
                try
                {
                    // double weight = Application::deviceHolder->getPlatformScale("b11ceeb5-af42d80f")->reading();
                    // j["地磅读数"] = weight;
                    // Application::deviceHolder->getSoundColumn( "b11ceeb5-af42d80f")->play("欢迎光临");
                    // auto reportPassResult = Application::reportSvr->reportPass("苏K85076", "b11ceeb5-af42d80f", "");
                } catch (std::exception &e)
                {
                    LOGGER_ERROR("错误", e.what());
                    j["错误"] = "";
                }
                catch (...)
                {
                    LOGGER_ERROR("错误");
                    j["错误"] = "";
                }
                res.set_content(j.dump(), "application/json");
            });

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

                    auto [succ,recognizeResult] = GenericBarrier::parseRecognizeResult(body);
                    if (!succ)
                    {
                        LOGGER_ERROR("车牌识别结果解析失败");
                        res.set_content(R"({})", "application/json");
                        return;
                    }
                    auto serialno = recognizeResult.alarmInfoPlate.serialno;
                    auto imageFile = recognizeResult.alarmInfoPlate.result.plateResult.imageFile;
                    auto license = recognizeResult.alarmInfoPlate.result.plateResult.license;
                    if (serialno.empty() || imageFile.empty() || license.empty())
                    {
                        LOGGER_ERROR("车牌识别结果解析失败");
                        res.set_content(R"({})", "application/json");
                        return;
                    }
                    if (license.find("无") != std::string::npos)
                    {
                        LOGGER_WARN("车牌识别失败");
                        res.set_content(R"({})", "application/json");
                        return;
                    }
                    /*auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
                    auto key = serialno + license;
                    long long time = ms + 60000 * 5;
                    {
                        std::lock_guard<std::mutex> lock(cacheMutex);
                        std::erase_if(cache, [time](const auto &pair)
                        {
                            return pair.second >= time;
                        });
                        if (cache.contains(key))
                        {
                            if (cache[key] < time)
                            {
                                res.set_content(R"({})", "application/json");
                                LOGGER_INFO("重复的车牌识别结果：{}  {}", serialno, license);
                                return;
                            }
                        }
                        cache[key] = ms;
                    }*/

                    Application::threadPool->submit([imageFile,license, barrier = Application::deviceHolder->getBarrier(serialno)]
                    {
                        if (!barrier)
                        {
                            LOGGER_ERROR("未找到对应的道闸");
                            return;
                        }
                        barrier->resolveRecognizeResult(license, imageFile);
                    });
                } 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>(getTime());
        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("未知异常，开门失败");
        }
    }

    std::tuple<bool, RecognizeResult> GenericBarrier::parseRecognizeResult(const std::string &jsonStr)
    {
        try
        {
            auto json = nlohmann::json::parse(jsonStr);
            RecognizeResult result = json.get<RecognizeResult>();
            return std::make_tuple(true, result);
        } catch (std::exception &e)
        {
            LOGGER_ERROR("未找到车牌识别结果：{}", e.what());
            return std::make_tuple(false, RecognizeResult());
        }catch (...)
        {
            LOGGER_ERROR("未知异常，未找到车牌识别结果");
            return std::make_tuple(false, RecognizeResult());
        }
    }

    void GenericBarrier::resolveRecognizeResult(const std::string &license, const std::string &imageFile)
    {
        try
        {
            LOGGER_INFO("车牌识别结果处理中，设备名称：{}", config.name);
            // 播放语音 1
            /*Application::threadPool->submit([sn = config.sn]
            {
                Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临");
            });*/
            std::thread t1([sn = config.sn]
            {
                Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临");
            });
            // 上传车头照
            auto imageFileDecode = SysUtil::base64_decode(imageFile);
            const std::shared_ptr<std::istream> imageFileStream = std::make_shared<std::istringstream>(imageFileDecode, std::ios_base::in | std::ios_base::binary);
            auto photoId = Snowflake::genIdStr();
            auto now = std::chrono::system_clock::now();
            std::time_t currentTime = std::chrono::system_clock::to_time_t(now);

            std::tm *localTime = std::localtime(&currentTime);

            std::stringstream ss;
            ss << std::put_time(localTime, "%Y%m%d");

            std::string date = ss.str();
            auto [carFrontUrlSucc,carFrontUrl] = Application::oss->upload(date + "/" + license + "_front_" + photoId + ".jpg", *imageFileStream);
            // 上报 1
            auto reportPassResult = Application::reportSvr->reportPass(license, config.sn, carFrontUrl);
            if (!(reportPassResult.code == 0 && (reportPassResult.data.type == 1 || reportPassResult.data.type == 2)))
            {
                LOGGER_INFO("不处理：{}", config.name);
                t1.join();
                return;
            }
            // 上报结果不为 0，播放语音 2 并读地磅，拍车斗并上传
            /*Application::threadPool->submit([sn =config.sn]
            {
                Application::deviceHolder->getSoundColumn(sn)->play("正在称重，请稍后");
            });*/
            std::thread t2([sn = config.sn]
            {
                Application::deviceHolder->getSoundColumn(sn)->play("正在称重，请稍后");
            });
            double weight = Application::deviceHolder->getPlatformScale(config.sn)->reading();
            auto photographPath = Application::deviceHolder->getVidicon(config.sn)->photograph();
            auto [carBodyUrlSucc,carBodyUrl] = Application::oss->upload(date + "/" + license + "_body_" + photoId + ".jpg", photographPath);
            if (!photographPath.empty())
            {
                auto pathPtr = SysUtil::ch_vlt(photographPath.c_str());
                if (std::remove(pathPtr.get()) != 0)
                {
                    auto [_,code,msg] = SysUtil::getError();
                    LOGGER_ERROR("临时照片删除失败：{}，错误码：{}，错误信息：{}", photographPath, code, msg);
                } else
                {
                    LOGGER_ERROR("临时照片删除成功：{}", photographPath);
                }
            }

            // 上报 2
            Application::reportSvr->report(license, config.sn, weight, carFrontUrl, carBodyUrl, reportPassResult.data.orderNo);
            t1.join();
            t2.join();
        } catch (std::exception &e)
        {
            LOGGER_ERROR("车牌识别结果处理失败: {}", e.what());
        }
        catch (...)
        {
            LOGGER_ERROR("未知异常，车牌识别结果处理失败");
        }
    }
} // zsy
