
#include "av_muxer.h"

#include <QDebug>
#include <chrono>
#include <errno.h>

bool AvMuxer::Open(std::string_view filePath, std::string_view format)
{
    Close();
    _isOpenFile = false;
    _filePath = filePath;
    
    qDebug() << "AvMuxer::Open: Opening file" << QString::fromStdString(_filePath) << "with format" << QString::fromStdString(std::string(format));
    
    int ret = avformat_alloc_output_context2(&_fmtCtx, nullptr, format.data(), _filePath.c_str());
    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        qDebug() << "AvMuxer::Open failed: Cannot allocate output context. Error:" << errbuf << "(" << ret << ")";
        return false;
    }
    
    if (!_fmtCtx) {
        qDebug() << "AvMuxer::Open failed: Format context is null after allocation";
        return false;
    }
    
    qDebug() << "AvMuxer::Open: Format context created successfully";
    qDebug() << "Output format:" << _fmtCtx->oformat->name << "long name:" << _fmtCtx->oformat->long_name;
    qDebug() << "Video codec:" << avcodec_get_name(_fmtCtx->oformat->video_codec);
    qDebug() << "Audio codec:" << avcodec_get_name(_fmtCtx->oformat->audio_codec);
    
    return true;
}

bool AvMuxer::WriteHeader()
{
    // av_dump_format(_fmtCtx, 0, _filePath.data(), 1);
    
    // 检查格式上下文是否有效
    if (!_fmtCtx) {
        qDebug() << "WriteHeader failed: Format context is null";
        return false;
    }
    
    // 检查输出格式是否有效
    if (!_fmtCtx->oformat) {
        qDebug() << "WriteHeader failed: Output format is null";
        return false;
    }
    
    qDebug() << "WriteHeader: Opening file" << QString::fromStdString(_filePath) << "with format" << _fmtCtx->oformat->name;
    
    // 检查是否为RTMP流
    bool isRtmpStream = _filePath.find("rtmp://") == 0;
    if (isRtmpStream) {
        qDebug() << "WriteHeader: Detected RTMP stream, setting network options";
        
        // 解析RTMP URL获取服务器信息
        std::string rtmpUrl = _filePath;
        std::string serverInfo = "Unknown";
        size_t protocolEnd = rtmpUrl.find("://");
        if (protocolEnd != std::string::npos) {
            size_t serverStart = protocolEnd + 3;
            size_t serverEnd = rtmpUrl.find("/", serverStart);
            if (serverEnd != std::string::npos) {
                serverInfo = rtmpUrl.substr(serverStart, serverEnd - serverStart);
            }
        }
        qDebug() << "WriteHeader: RTMP server:" << QString::fromStdString(serverInfo);
        
        // 设置RTMP连接选项
        AVDictionary* options = nullptr;
        av_dict_set(&options, "rtmp_live", "1", 0);  // 设置为直播模式
        av_dict_set(&options, "rtmp_buffer", "1000", 0);  // 设置缓冲区大小
        av_dict_set(&options, "timeout", "3000000", 0);  // 设置连接超时(3秒，单位微秒)
        av_dict_set(&options, "rw_timeout", "3000000", 0);  // 设置读写超时(3秒)
        av_dict_set(&options, "stimeout", "3000000", 0);  // 设置socket超时(3秒)
        av_dict_set(&options, "rtmp_conn", "S:publish", 0);  // 设置发布模式
        av_dict_set(&options, "rtmp_tcurl", _filePath.c_str(), 0);  // 设置RTMP URL
        av_dict_set(&options, "rtmp_flashver", "FMLE/3.0 (compatible; FMSc/1.0)", 0);  // 设置Flash版本
        
        qDebug() << "WriteHeader: RTMP options set - live mode, 1000ms buffer, 3s timeout";
        qDebug() << "WriteHeader: Multiple timeout mechanisms enabled (timeout, rw_timeout, stimeout)";
        
        // 打开RTMP流
        if (!(_fmtCtx->oformat->flags & AVFMT_NOFILE)) {
            qDebug() << "WriteHeader: Attempting RTMP connection with enhanced timeout protection...";
            
            // 记录开始时间用于额外的超时检查
            auto startTime = std::chrono::steady_clock::now();

            int ret = avio_open(&_fmtCtx->pb, _filePath.c_str(), AVIO_FLAG_WRITE);

            // 检查连接时间
            auto endTime = std::chrono::steady_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
            qDebug() << "WriteHeader: RTMP connection attempt took" << duration.count() << "ms";
            if (ret < 0) {
                char errbuf[AV_ERROR_MAX_STRING_SIZE];
                av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
                qDebug() << "WriteHeader failed: Cannot connect to RTMP server" << QString::fromStdString(_filePath);
                qDebug() << "Error:" << errbuf << "(" << ret << ")";
                qDebug() << "RTMP server:" << QString::fromStdString(serverInfo);
                qDebug() << "Connection duration:" << duration.count() << "ms";
                
                // 检查是否为超时相关问题
                if (duration.count() >= 2900) {  // 接近3秒超时
                    qDebug() << "WARNING: Connection attempt took nearly full timeout period!";
                    qDebug() << "This suggests network connectivity issues or server overload.";
                }
                
                // 针对特定错误码提供详细解释
                if (ret == -10049) {
                    qDebug() << "Error Analysis: WSAEADDRNOTAVAIL - Address not available";
                    qDebug() << "This error typically means:";
                    qDebug() << "- The RTMP server is not running or not listening on port 1935";
                    qDebug() << "- The IP address 192.168.3.76 is not reachable from this machine";
                    qDebug() << "- Network routing issues or firewall blocking";
                } else if (ret == -111) {
                    qDebug() << "Error Analysis: Connection refused";
                    qDebug() << "The server actively refused the connection";
                } else if (ret == -110 || ret == -ETIMEDOUT) {
                    qDebug() << "Error Analysis: Connection timeout";
                    qDebug() << "The server did not respond within the timeout period";
                    qDebug() << "Consider increasing timeout or checking network latency";
                } else if (ret == -ECONNRESET) {
                    qDebug() << "Error Analysis: Connection reset by peer";
                    qDebug() << "The server closed the connection unexpectedly";
                }
                
                qDebug() << "Enhanced Troubleshooting steps:";
                qDebug() << "1. Quick network test: ping -n 1 -w 1000" << QString::fromStdString(serverInfo.substr(0, serverInfo.find(':')));
                qDebug() << "2. Port connectivity: telnet" << QString::fromStdString(serverInfo) << "(should connect immediately)";
                qDebug() << "3. Verify RTMP server status and logs on" << QString::fromStdString(serverInfo);
                qDebug() << "4. Check if server accepts RTMP publish connections";
                qDebug() << "5. Test with reduced timeout: try 1-2 second timeout first";
                qDebug() << "6. Verify stream key and application name in URL";
                qDebug() << "7. Check server-side connection limits and authentication";
                qDebug() << "8. Test with FFmpeg: ffmpeg -re -f lavfi -i testsrc=duration=10:size=320x240:rate=30 -c:v libx264 -f flv" << QString::fromStdString(_filePath);
                av_dict_free(&options);
                return false;
            }
            av_dict_free(&options);
            qDebug() << "WriteHeader: RTMP connection established successfully to" << QString::fromStdString(serverInfo);
        }
    } else {
        // 打开本地文件
        if (!(_fmtCtx->oformat->flags & AVFMT_NOFILE)) {
            int ret = avio_open(&_fmtCtx->pb, _filePath.c_str(), AVIO_FLAG_WRITE);
            if (ret < 0) {
                char errbuf[AV_ERROR_MAX_STRING_SIZE];
                av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
                qDebug() << "WriteHeader failed: Cannot open output file" << QString::fromStdString(_filePath) 
                         << "Error:" << errbuf << "(" << ret << ")";
                return false;
            }
            qDebug() << "WriteHeader: Local file opened successfully";
        }
    }
    
    // 写入文件头
    int ret = avformat_write_header(_fmtCtx, nullptr);
    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        qDebug() << "WriteHeader failed: Cannot write header. Error:" << errbuf << "(" << ret << ")";
        
        // 输出流信息用于调试
        qDebug() << "Format context info:";
        qDebug() << "  - Number of streams:" << _fmtCtx->nb_streams;
        for (unsigned int i = 0; i < _fmtCtx->nb_streams; i++) {
            AVStream* stream = _fmtCtx->streams[i];
            qDebug() << "  - Stream" << i << "codec:" << avcodec_get_name(stream->codecpar->codec_id)
                     << "type:" << av_get_media_type_string(stream->codecpar->codec_type);
        }
        return false;
    }
    
    qDebug() << "WriteHeader: Header written successfully";
    // _fmtCtx->flags |= AVFMT_FLAG_NOBUFFER;  // 无缓冲
    // _fmtCtx->flags |= AVFMT_FLAG_FLUSH_PACKETS;  // 立即刷新数据包
    _isOpenFile = true;
    return true;
}

int AvMuxer::AddVideoStream(const Encoder<MediaType::VIDEO>::Param& param)
{
    if (_fmtCtx->oformat->video_codec == AV_CODEC_ID_NONE) {
        qDebug() << "AddVideoStream failed: Output format does not support video";
        return -1;
    }
    
    qDebug() << "AddVideoStream: Creating video encoder with codec" << avcodec_get_name(_fmtCtx->oformat->video_codec)
             << "resolution" << param.width << "x" << param.height << "fps" << param.fps;
    
    Info info;
    info.pts = 0;
    info.fps = param.fps;
    
    // 获取可用编码器列表
    const auto& usableEncoders = Encoder<MediaType::VIDEO>::GetUsableEncoders();
    if (usableEncoders.empty()) {
        qDebug() << "AddVideoStream failed: No usable video encoders found";
        return -1;
    }
    
    qDebug() << "AddVideoStream: Available encoders:" << usableEncoders.size();
    for (const auto& enc : usableEncoders) {
        qDebug() << "  - " << QString::fromStdString(enc);
    }
    
    // 尝试使用指定的编码器，如果失败则自动回退
    auto encoder = new Encoder<MediaType::VIDEO>;
    auto modifiableParam = param; // 创建可修改的副本
    
    // 首先尝试用户指定的编码器
    bool encoderOpened = false;
    std::string usedEncoder = modifiableParam.name;
    
    if (!modifiableParam.name.empty()) {
        qDebug() << "AddVideoStream: Trying user specified encoder:" << QString::fromStdString(modifiableParam.name);
        if (encoder->Open(modifiableParam, _fmtCtx)) {
            encoderOpened = true;
            qDebug() << "AddVideoStream: Successfully opened user specified encoder:" << QString::fromStdString(modifiableParam.name);
        } else {
            qDebug() << "AddVideoStream: User specified encoder failed:" << QString::fromStdString(modifiableParam.name);
        }
    }
    
    // 如果指定编码器失败或未指定，尝试可用编码器列表
    if (!encoderOpened) {
        qDebug() << "AddVideoStream: Trying fallback encoders...";
        for (const auto& encoderName : usableEncoders) {
            if (encoderName == modifiableParam.name) {
                continue; // 跳过已经尝试过的编码器
            }
            
            modifiableParam.name = encoderName;
            qDebug() << "AddVideoStream: Trying fallback encoder:" << QString::fromStdString(encoderName);
            
            if (encoder->Open(modifiableParam, _fmtCtx)) {
                encoderOpened = true;
                usedEncoder = encoderName;
                qDebug() << "AddVideoStream: Successfully opened fallback encoder:" << QString::fromStdString(encoderName);
                break;
            } else {
                qDebug() << "AddVideoStream: Fallback encoder failed:" << QString::fromStdString(encoderName);
            }
        }
    }
    
    if (!encoderOpened) {
        qDebug() << "AddVideoStream failed: All video encoders failed to open";
        delete encoder;
        return -1;
    }
    
    info.type = MediaType::VIDEO;
    info.encoder = encoder;
    
    if (!_AddStream(info)) {
        qDebug() << "AddVideoStream failed: Cannot add video stream to format context";
        delete encoder;
        return -1;
    }
    
    _infos.back().stream->time_base = {1, info.fps};
    qDebug() << "AddVideoStream: Video stream added successfully with encoder:" << QString::fromStdString(usedEncoder) << "index:" << info.streamIndex;
    return info.streamIndex;
}

int AvMuxer::AddAudioStream(const Encoder<MediaType::AUDIO>::Param& param)
{
    if (_fmtCtx->oformat->audio_codec == AV_CODEC_ID_NONE) {
        qDebug() << "AddAudioStream failed: Output format does not support audio";
        return -1;
    }

    qDebug() << "AddAudioStream: Creating audio encoder with codec"
             << avcodec_get_name(_fmtCtx->oformat->audio_codec);

    // 获取可用编码器列表
    const auto& usableEncoders = Encoder<MediaType::AUDIO>::GetUsableEncoders();

    Info info;
    info.pts = 0;
    info.fps = AUDIO_SAMPLE_RATE;

    auto encoder = new Encoder<MediaType::AUDIO>;
    auto modifiableParam = param; // 创建可修改的副本

    bool encoderOpened = false;
    std::string usedEncoder = modifiableParam.name;

    // 首先尝试用户指定的编码器
    if (!modifiableParam.name.empty()) {
        qDebug() << "AddAudioStream: Trying user specified encoder:" << QString::fromStdString(modifiableParam.name);
        if (encoder->Open(modifiableParam, _fmtCtx)) {
            encoderOpened = true;
            qDebug() << "AddAudioStream: Successfully opened user specified encoder:" << QString::fromStdString(modifiableParam.name);
        } else {
            qDebug() << "AddAudioStream: User specified encoder failed:" << QString::fromStdString(modifiableParam.name);
        }
    }

    // 如果指定编码器失败，尝试可用编码器列表
    if (!encoderOpened) {
        qDebug() << "AddAudioStream: Trying fallback encoders...";

        // 构建回退列表
        std::vector<std::string> fallbackList;
        if (!usableEncoders.empty()) {
            fallbackList = usableEncoders;
        } else {
            // 基础回退集合
            fallbackList = {"aac", "libfdk_aac", "libopus", "opus", "libmp3lame"};
        }

        for (const auto& encoderName : fallbackList) {
            if (!modifiableParam.name.empty() && encoderName == modifiableParam.name) {
                continue; // 跳过已经尝试过的编码器
            }

            modifiableParam.name = encoderName;
            qDebug() << "AddAudioStream: Trying fallback encoder:" << QString::fromStdString(encoderName);

            if (encoder->Open(modifiableParam, _fmtCtx)) {
                encoderOpened = true;
                usedEncoder = encoderName;
                qDebug() << "AddAudioStream: Successfully opened fallback encoder:" << QString::fromStdString(encoderName);
                break;
            } else {
                qDebug() << "AddAudioStream: Fallback encoder failed:" << QString::fromStdString(encoderName);
            }
        }
    }

    // 如果仍未打开成功，最后尝试自动按容器默认/AAC回退（通过传空name让内部回退）
    if (!encoderOpened) {
        qDebug() << "AddAudioStream: Trying auto/default encoder as last resort...";
        modifiableParam.name.clear();
        if (encoder->Open(modifiableParam, _fmtCtx)) {
            encoderOpened = true;
            usedEncoder = encoder->GetCtx() && encoder->GetCtx()->codec ? encoder->GetCtx()->codec->name : std::string("<auto>");
            qDebug() << "AddAudioStream: Auto/default encoder opened successfully:" << (encoder->GetCtx() && encoder->GetCtx()->codec ? encoder->GetCtx()->codec->name : "unknown");
        }
    }

    if (!encoderOpened) {
        qDebug() << "AddAudioStream failed: All audio encoders failed to open";
        delete encoder;
        return -1;
    }

    info.type = MediaType::AUDIO;
    info.encoder = encoder;

    if (!_AddStream(info)) {
        qDebug() << "AddAudioStream failed: Cannot add audio stream to format context";
        delete encoder;
        return -1;
    }

    _infos.back().stream->time_base = {1, AUDIO_SAMPLE_RATE};
    qDebug() << "AddAudioStream: Audio stream added successfully with encoder:" << QString::fromStdString(usedEncoder) << "index:" << info.streamIndex;
    return info.streamIndex;
}

bool AvMuxer::Write(AVFrame* frame, int streamIndex, bool isEnd)
{
    std::lock_guard<std::mutex> lk(_mtx);
    if (!(streamIndex >= 0 && _infos.size() > (size_t)streamIndex)) { return false; }
    auto&& info = _infos[streamIndex];
    if (info.isEnd) {
        return true;
    }
    if (isEnd) {
        info.isEnd = isEnd;
        frame = nullptr;
    }
    if (!info.encoder) { return false; }

    // 只在有多个活跃流且音频流有数据时才做同步检查
    int activeStreamCount = 0;
    int audioStreamIdx = -1;
    for (int i = 0; i < _infos.size(); ++i) {
        if (!_infos[i].isEnd && _infos[i].pts > 0) {
            ++activeStreamCount;
            if (_infos[i].type == MediaType::AUDIO) audioStreamIdx = i;
        }
    }
    // 只在视频流超前音频流时丢帧，音频流超前时不阻塞视频帧写入
    if (activeStreamCount > 1 && audioStreamIdx != -1) {
        double curTime = double(info.pts) / info.fps;
        double audioTime = double(_infos[audioStreamIdx].pts) / _infos[audioStreamIdx].fps;
        if (info.type == MediaType::VIDEO && curTime - audioTime > 0.7) {
            // 视频流超前音频流太多才丢帧
            return false;
        }
    }
    info.isEncodeOverload = false;
    if (!info.encoder->PushFrame(frame, isEnd, info.pts)) {
        return false;
    }
    // 调试输出音视频流的pts
    // if (info.type == MediaType::AUDIO) {
    //     qDebug() << "AUDIO PTS:" << info.pts;
    // } else if (info.type == MediaType::VIDEO) {
    //     qDebug() << "VIDEO PTS:" << info.pts;
    // }
    info.pts += info.type == MediaType::AUDIO ? info.encoder->GetCtx()->frame_size : 1; // 更新 pts
    AVPacket* packet = nullptr;
    while ((packet = info.encoder->Encode())) {
        av_packet_rescale_ts(packet, info.encoder->GetCtx()->time_base, info.stream->time_base);
        packet->stream_index = info.stream->index;
        if (av_interleaved_write_frame(_fmtCtx, packet) < 0) {
            return false;
        }
    }
    info.encoder->AfterEncode();
    return true;
}

bool AvMuxer::_CheckTime(double time)
{
    auto minTime = double(_infos.front().pts) / _infos.front().fps;
    for (int idx = 1; idx < _infos.size(); ++idx) {
        minTime = std::min(double(_infos[idx].pts) / _infos[idx].fps, minTime);
    }
    if (time - minTime > 0.3) { // 放宽到0.3秒
        qDebug() << "丢帧: 当前流时间:" << time << "最慢流时间:" << minTime << "差值:" << (time - minTime);
        return false;
    }
    return true;
}

void AvMuxer::Close()
{
    if (_fmtCtx == nullptr) {
        return;
    }
    // 清空编码器缓存
    for (int index = 0; index < _infos.size(); ++index) {
        __DebugPrint("stream: %d, time:%f", index, double(_infos[index].pts) / _infos[index].fps);
    }
    if (_isOpenFile) {
        int tr = av_write_trailer(_fmtCtx);
        if (tr < 0) { __DebugPrint("av_write_trailer failed: %d", tr); }
        Free(_fmtCtx->pb, [this] { avio_closep(&_fmtCtx->pb); });
    }
    _isOpenFile = false;

    for (auto&& info : _infos) {
        Free(info.encoder, [&info] { info.encoder->Close(); delete info.encoder; });
    }
    _infos.clear();
    Free(_fmtCtx, [this] { avformat_free_context(_fmtCtx); });
}

bool AvMuxer::_AddStream(Info& info)
{
    info.stream = avformat_new_stream(_fmtCtx, nullptr);
    if (!info.stream) {
        qDebug() << "_AddStream failed: Cannot create new stream";
        return false;
    }
    
    info.stream->id = _fmtCtx->nb_streams - 1;
    
    int ret = avcodec_parameters_from_context(info.stream->codecpar, info.encoder->GetCtx());
    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        qDebug() << "_AddStream failed: Cannot copy codec parameters. Error:" << errbuf << "(" << ret << ")";
        return false;
    }
    
    info.streamIndex = _fmtCtx->nb_streams - 1;
    info.pts = 0;
    info.isEnd = false;
    _infos.push_back(info);
    
    qDebug() << "_AddStream: Stream added successfully, index:" << info.streamIndex;
    return true;
}

AVCodecContext* AvMuxer::GetCodecCtx(int streamIndex)
{
    if (!(streamIndex >= 0 && _infos.size() > (size_t)streamIndex)) { __DebugPrint("GetCodecCtx invalid streamIndex: %d", streamIndex); return nullptr; }
    return _infos[streamIndex].encoder->GetCtx();
}

bool AvMuxer::IsEncodeOverload() const
{
    for (auto&& info : _infos) {
        if (info.isEncodeOverload) {
            return true;
        }
    }
    return false;
}
