#include "wbs.h"

#include "app/app.h"
#include "mqtt/mqtt_cli.h"
#include "mqtt/mqtt_msg.h"
#include "loadometer/loadometer.h"
#include "spkbox/spkbox.h"
#include "vidicon/vidicon.h"

namespace zsy
{
    Wbs::Wbs(const WbsConfig &config) : config(config), bizLock(std::make_shared<BizLock>())
    {
        reportInitializing();
        for (const auto &device_group_config: config.device_groups())
        {
            deviceGroups[device_group_config.sn()] = std::make_shared<DeviceGroup>(device_group_config);
        }
        if (const auto mqttCli = MQTTCli::getRemoteCli())
        {
            const auto appSn = App::sn();
            mqttCli->subscribe(appSn + "/wbs/obtain_truck_data", [this](const MQTTMsg &msg)
            {
                LOGGER_INFO("收到获取车辆数据的请求：{}", msg.payload);
                const auto obtainTruckDataParamopt = JSON::parse<ObtainTruckDataParam>(msg.payload);
                if (!obtainTruckDataParamopt)
                {
                    LOGGER_ERROR("消息解析失败");
                    return;
                }
                obtainTruckData(obtainTruckDataParamopt.value());
            });
            mqttCli->subscribe(appSn + "/wbs/play_voice", [this](const MQTTMsg &msg)
            {
                LOGGER_INFO("收到播放语音的请求：{}", msg.payload);
                auto playVoiceParamOpt = JSON::parse<PlayVoiceParam>(msg.payload);
                if (!playVoiceParamOpt)
                {
                    LOGGER_ERROR("消息解析失败");
                    return;
                }
                playVoice(playVoiceParamOpt.value());
            });
            mqttCli->subscribe(appSn + "/wbs/open_door", [this](const MQTTMsg &msg)
            {
                LOGGER_INFO("收到打开道闸的请求：{}", msg.payload);

                const auto paramOpt = JSON::parse<OpenDoorParam>(msg.payload);
                if (!paramOpt)
                {
                    LOGGER_ERROR("消息解析失败");
                    return;
                }
                openDoor(paramOpt.value());
            });
        }
        reportAvailable();
        waitStarted();
    }

    void Wbs::playVoice(const PlayVoiceParam &param)
    {
        std::shared_lock lock(WBS_MTX);
        if (deviceGroups.contains(param.groupSn))
        {
            const auto deviceGroup = deviceGroups.at(param.groupSn);
            if (!deviceGroup)
            {
                LOGGER_ERROR("设备不存在：组编号：{}", param.groupSn);
                return;
            }
            if (const auto spkbox = deviceGroup->getSpkbox(param.barrierSn); spkbox)
            {
                spkbox->play(param.content, param.times);
            } else
            {
                LOGGER_ERROR("音箱不存在：道闸编号：{}", param.barrierSn);
            }
        }
    }

    void Wbs::openDoor(const OpenDoorParam &param)
    {
        std::shared_lock lock(WBS_MTX);
        if (deviceGroups.contains(param.groupSn))
        {
            auto deviceGroup = deviceGroups.at(param.groupSn);
            if (!deviceGroup)
            {
                LOGGER_ERROR("设备不存在：组编号：{}", param.groupSn);
                return;
            }
            if (const auto barrier = deviceGroup->getBarrier(param.barrierSn); barrier)
            {
                barrier->open();
            } else
            {
                LOGGER_ERROR("道闸不存在：道闸编号：{}", param.barrierSn);
            }
        }
    }

    void Wbs::obtainTruckData(const ObtainTruckDataParam &param)
    {
        LOCK_BIZ(param.groupSn)
        {
            std::shared_lock lock(WBS_MTX);

            ObtainTruckDataResult obtainTruckDataResult{
                .id = param.id,
                .orderId = param.orderId,
                .sn = App::sn(),
                .stationSn = App::stationSn(),
                .groupSn = param.groupSn,
                .barrierSn = param.barrierSn,
                .success = false,
            };
            if (deviceGroups.contains(param.groupSn))
            {
                const auto deviceGroup = deviceGroups.at(param.groupSn);
                if (!deviceGroup)
                {
                    LOGGER_ERROR("设备不存在：组编号：{}", param.groupSn);
                    return;
                }
                if (const auto ytj = deviceGroup->getBarrier(param.barrierSn); ytj)
                {
                    obtainTruckDataResult.frontImage = ytj->photograph();
                } else
                {
                    LOGGER_ERROR("道闸不存在：道闸编号：{}", param.barrierSn);
                }
                if (const auto vidicon = deviceGroup->getVidicon(param.barrierSn); vidicon)
                {
                    obtainTruckDataResult.bodyImage = vidicon->photograph();
                } else
                {
                    LOGGER_ERROR("摄像机不存在：道闸编号：{}", param.barrierSn);
                }
                if (const auto loadometer = deviceGroup->getLoadometer(param.barrierSn); loadometer)
                {
                    obtainTruckDataResult.weight = loadometer->currentWeight();
                } else
                {
                    LOGGER_ERROR("摄像机不存在：道闸编号：{}", param.barrierSn);
                }
            }
            obtainTruckDataResult.success = obtainTruckDataResult.weight > 0 && !obtainTruckDataResult.bodyImage.empty() && !obtainTruckDataResult.frontImage.empty();
            if (const auto mqttCli = MQTTCli::getRemoteCli())
            {
                mqttCli->publish("wbs/obtain_truck_data/reply", JSON::toJsonStr(obtainTruckDataResult));
            }
        }
        else
        {
            LOGGER_ERROR("当前已有业务正在获取数据：组编号：{}，业务ID：{}", param.groupSn, param.id);
        }
    }

    std::string Wbs::name() const noexcept
    {
        return config.name();
    }

    std::string Wbs::sn() const noexcept
    {
        return config.sn();
    }

    void Wbs::init()
    {
        std::unique_lock lock(WBS_MTX);
        if (!App::APP_CONFIG->has_wbs_config())return;
        WBS = std::make_shared < Wbs > (App::APP_CONFIG->wbs_config());
    }

    void Wbs::destroy()
    {
        std::unique_lock lock(WBS_MTX);
        WBS.reset();
        WBS = nullptr;
    }

    std::shared_ptr<Wbs> Wbs::get()
    {
        std::shared_lock lock(WBS_MTX);
        return WBS;
    }
} // zsy