/*
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <avcodec_sysevent.h>
#include <unistd.h>
#include <unordered_map>
#include <mutex>
#include <cstring>
#include "avcodec_log.h"
#include "avcodec_errors.h"
#include <openssl/evp.h>
#include "hisysevent.h"
#include <nlohmann/json.hpp>

using Json = nlohmann::json;
namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "AVCodecSysEvent"};
constexpr char HISYSEVENT_DOMAIN_AVCODEC[] = "AV_CODEC";

const std::unordered_map<OHOS::MediaAVCodec::FaultType, std::string> FAULT_TYPE_TO_STRING = {
    {OHOS::MediaAVCodec::FaultType::FAULT_TYPE_FREEZE,          "Freeze"},
    {OHOS::MediaAVCodec::FaultType::FAULT_TYPE_CRASH,           "Crash"},
    {OHOS::MediaAVCodec::FaultType::FAULT_TYPE_INNER_ERROR,     "Inner error"},
};
constexpr static int32_t SOURCE_STATISTICS_REPORT_HOURS = 4;
} // namespace

namespace OHOS {
namespace MediaAVCodec {
void FaultEventWrite(FaultType faultType, const std::string& msg, const std::string& module)
{
    CHECK_AND_RETURN_LOG(faultType >= FaultType::FAULT_TYPE_FREEZE && faultType < FaultType::FAULT_TYPE_END,
        "Invalid fault type: %{public}d", faultType);
    HiSysEventWrite(HISYSEVENT_DOMAIN_AVCODEC, "FAULT",
                    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
                    "MODULE", module,
                    "FAULTTYPE", FAULT_TYPE_TO_STRING.at(faultType),
                    "MSG", msg);
}

void ServiceStartEventWrite(uint32_t useTime, const std::string& module)
{
    uint64_t useMemory = 5000;
    HiSysEventWrite(HISYSEVENT_DOMAIN_AVCODEC, "SERVICE_START_INFO",
                    OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR, "MODULE", module.c_str(), "TIME", useTime,
                    "MEMORY", useMemory);
}

void StreamAppPackageNameEventWrite(const std::string& sysCap,
    const std::string& packageName, const std::string& apiCall, const std::string& mediaEvents)
{
    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA, "MEDIAKIT_STATISTICS",
                    OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
                    "SYSCAP",             sysCap,
                    "APP_NAME",           packageName,
                    "INSTANCE_ID",        "",
                    "API_CALL",           apiCall,
                    "MEDIA_EVENTS",       mediaEvents);
}

void CodecStartEventWrite(CodecDfxInfo& codecDfxInfo)
{
    HiSysEventWrite(HISYSEVENT_DOMAIN_AVCODEC, "CODEC_START_INFO",
                    OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
                    "CLIENT_PID",           codecDfxInfo.clientPid,
                    "CLIENT_UID",           codecDfxInfo.clientUid,
                    "CODEC_INSTANCE_ID",    codecDfxInfo.codecInstanceId,
                    "CODEC_NAME",           codecDfxInfo.codecName,
                    "CODEC_IS_VENDOR",      codecDfxInfo.codecIsVendor,
                    "CODEC_MODE",           codecDfxInfo.codecMode,
                    "ENCODER_BITRATE",      codecDfxInfo.encoderBitRate,
                    "VIDEO_WIDTH",          codecDfxInfo.videoWidth,
                    "VIDEO_HEIGHT",         codecDfxInfo.videoHeight,
                    "VIDEO_FRAMERATE",      codecDfxInfo.videoFrameRate,
                    "VIDEO_PIXEL_FORMAT",   codecDfxInfo.videoPixelFormat,
                    "AUDIO_CHANNEL_COUNT",  codecDfxInfo.audioChannelCount,
                    "AUDIO_SAMPLE_RATE",    codecDfxInfo.audioSampleRate);
}

void CodecStopEventWrite(pid_t clientPid, uid_t clientUid, int32_t codecInstanceId)
{
    HiSysEventWrite(HISYSEVENT_DOMAIN_AVCODEC, "CODEC_STOP_INFO",
                    OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
                    "CLIENT_PID", clientPid, "CLIENT_UID", clientUid, "CODEC_INSTANCE_ID", codecInstanceId);
}

void FaultDemuxerEventWrite(DemuxerFaultInfo& demuxerFaultInfo)
{
    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA, "DEMUXER_FAILURE",
                    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
                    "APP_NAME",         demuxerFaultInfo.appName,
                    "INSTANCE_ID",      demuxerFaultInfo.instanceId,
                    "CALLER_TYPE",      demuxerFaultInfo.callerType,
                    "SOURCE_TYPE",      demuxerFaultInfo.sourceType,
                    "CONTAINER_FORMAT", demuxerFaultInfo.containerFormat,
                    "STREAM_TYPE",      demuxerFaultInfo.streamType,
                    "ERROR_MESG",       demuxerFaultInfo.errMsg);
}

void FaultAudioCodecEventWrite(AudioCodecFaultInfo& audioCodecFaultInfo)
{
    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA, "AUDIO_CODEC_FAILURE",
                    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
                    "APP_NAME",    audioCodecFaultInfo.appName,
                    "INSTANCE_ID", audioCodecFaultInfo.instanceId,
                    "CALLER_TYPE", audioCodecFaultInfo.callerType,
                    "AUDIO_CODEC", audioCodecFaultInfo.audioCodec,
                    "ERROR_MESG",  audioCodecFaultInfo.errMsg);
}

void FaultVideoCodecEventWrite(VideoCodecFaultInfo& videoCodecFaultInfo)
{
    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA, "VIDEO_CODEC_FAILURE",
                    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
                    "APP_NAME",    videoCodecFaultInfo.appName,
                    "INSTANCE_ID", videoCodecFaultInfo.instanceId,
                    "CALLER_TYPE", videoCodecFaultInfo.callerType,
                    "VIDEO_CODEC", videoCodecFaultInfo.videoCodec,
                    "ERROR_MESG",  videoCodecFaultInfo.errMsg);
}

void FaultMuxerEventWrite(MuxerFaultInfo& muxerFaultInfo)
{
    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA, "MUXER_FAILURE",
                    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
                    "APP_NAME",         muxerFaultInfo.appName,
                    "INSTANCE_ID",      muxerFaultInfo.instanceId,
                    "CALLER_TYPE",      muxerFaultInfo.callerType,
                    "VIDEO_CODEC",      muxerFaultInfo.videoCodec,
                    "AUDIO_CODEC",      muxerFaultInfo.audioCodec,
                    "CONTAINER_FORMAT", muxerFaultInfo.containerFormat,
                    "ERROR_MESG",       muxerFaultInfo.errMsg);
}

void FaultRecordAudioEventWrite(AudioSourceFaultInfo& audioSourceFaultInfo)
{
    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA, "RECORD_AUDIO_FAILURE",
                    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
                    "APP_NAME",          audioSourceFaultInfo.appName,
                    "INSTANCE_ID",       audioSourceFaultInfo.instanceId,
                    "AUDIO_SOURCE_TYPE", audioSourceFaultInfo.audioSourceType,
                    "ERROR_MESG",        audioSourceFaultInfo.errMsg);
}

void SourceStatisticsEventWrite(SourceStatisticsReportInfo& sourceReportInfo)
{
    static std::list<std::string> reportInfoList;
    static std::chrono::system_clock::time_point lastUpdateTime = std::chrono::system_clock::now();
    static std::mutex sourceMutex;

    std::lock_guard<std::mutex> lock(sourceMutex);
    Json json;
    uint8_t hash[EVP_MAX_MD_SIZE];
    uint32_t hashLen;
    EVP_Digest(sourceReportInfo.ca_.c_str(), sourceReportInfo.ca_.size(), hash, &hashLen, EVP_sha256(), NULL);
    json["APP_NAME"] = sourceReportInfo.appName_;
    json["MEDIA_EVENTS"]["SOURCE_TYPE"] = sourceReportInfo.sourceType_;
    json["MEDIA_EVENTS"]["SOURCE_URI"] = sourceReportInfo.sourceUri_;
    json["MEDIA_EVENTS"]["PLAY_STRATEGY_DURATION"] = sourceReportInfo.playStrategyDuration_;
    json["MEDIA_EVENTS"]["PLAY_STRATEGY_BUFFER_DURATION_FOR_PLAYING"] =
        sourceReportInfo.playStrateBufferDurationForPlaying_;
    json["MEDIA_EVENTS"]["BITRATE"] = sourceReportInfo.bitRate_;
    json["MEDIA_EVENTS"]["VIDEO_STREAM_CNT"] = sourceReportInfo.videoStreamCnt_;
    json["MEDIA_EVENTS"]["AUDIO_STREAM_CNT"] = sourceReportInfo.audioStreamCnt_;
    json["MEDIA_EVENTS"]["SUBTITLE_STREAM_CNT"] = sourceReportInfo.subtitleCnt_;
    json["MEDIA_EVENTS"]["CERT_ISSUER_NAMES"] = sourceReportInfo.ca_;
    json["MEDIA_EVENTS"]["CERT_HASH"] = hash;
    std::string jsonString = json.dump();
    reportInfoList.push_back(jsonString);
    auto currentTime = std::chrono::system_clock::now();
    auto diff = currentTime - lastUpdateTime;
    auto hour = std::chrono::duration_cast<std::chrono::hours>(diff).count();
    if (hour >= SOURCE_STATISTICS_REPORT_HOURS) {
        for (auto& report : reportInfoList) {
            HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::MULTI_MEDIA,
                "SOURCE_STATISTICS",
                OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
                "EVENTS", report);
        }
        reportInfoList.clear();
        lastUpdateTime = currentTime;
    }
}
} // namespace MediaAVCodec
} // namespace OHOS