//
// Created by Administrator on 2026/4/27.
//

#include "erpc/service/include/file_service_impl.h"
#include <fcntl.h>
#include <unistd.h>

FileServiceImpl::FileServiceImpl()
    : upload_mutex_(RT_NULL),
      upload_fd_(-1),
      upload_expected_size_(0),
      upload_received_size_(0)
{
    upload_mutex_ = rt_mutex_create("sv_file", RT_IPC_FLAG_PRIO);
}

FileServiceImpl::~FileServiceImpl()
{
    if (upload_mutex_) {
        rt_mutex_take(upload_mutex_, RT_WAITING_FOREVER);
        (void)abortUploadLocked();
        rt_mutex_release(upload_mutex_);
        rt_mutex_delete(upload_mutex_);
        upload_mutex_ = RT_NULL;
    }
}

bool FileServiceImpl::normalizeUploadPath(const char* path, std::string* normalized)
{
    std::string local_path;

    if (!path || !normalized) {
        return false;
    }

    local_path.assign(path);
    if (local_path.empty()) {
        return false;
    }

    if (local_path[0] != '/') {
        local_path.insert(local_path.begin(), '/');
    }

    // 避免覆盖设备节点，也避免通过 ".." 跳出预期目录。
    if (local_path == "/" ||
        local_path.rfind("/dev", 0) == 0 ||
        local_path.find("/../") != std::string::npos ||
        (local_path.size() >= 3 && local_path.compare(local_path.size() - 3, 3, "/..") == 0)) {
        return false;
    }

    *normalized = local_path;
    return true;
}

int32_t FileServiceImpl::posixErrorToRtError(void)
{
    const int err = rt_get_errno();

    if (err > 0) {
        return -err;
    }
    if (err < 0) {
        return err;
    }

    return -RT_ERROR;
}

void FileServiceImpl::resetUploadStateLocked(void)
{
    upload_fd_ = -1;
    upload_expected_size_ = 0;
    upload_received_size_ = 0;
    upload_target_path_.clear();
    upload_temp_path_.clear();
}

int32_t FileServiceImpl::abortUploadLocked(void)
{
    // 取消上传时尽量关闭文件、删除残留的 .part 文件，并清空内存状态。
    if (upload_fd_ >= 0) {
        if (::close(upload_fd_) != 0) {
            const int32_t err = posixErrorToRtError();
            upload_fd_ = -1;
            if (!upload_temp_path_.empty()) {
                (void)::unlink(upload_temp_path_.c_str());
            }
            resetUploadStateLocked();
            return err;
        }
    }

    if (!upload_temp_path_.empty()) {
        (void)::unlink(upload_temp_path_.c_str());
    }

    resetUploadStateLocked();
    return RT_EOK;
}

int32_t FileServiceImpl::fileWriteBegin(const char* path, uint32_t total_size)
{
    std::string normalized_path;
    std::string temp_path;
    int fd;

    if (!upload_mutex_) {
        rt_kprintf("[FileService][E] fileWriteBegin: upload mutex is null\n");
        return -RT_ENOMEM;
    }

    if (!normalizeUploadPath(path, &normalized_path)) {
        rt_kprintf("[FileService][E] fileWriteBegin: invalid path=%s\n",
                   path ? path : "(null)");
        return -RT_EINVAL;
    }

    temp_path = normalized_path + ".part";

    rt_mutex_take(upload_mutex_, RT_WAITING_FOREVER);
    // 单会话上传可以避免多个客户端同时写同一组上传状态。
    if (upload_fd_ >= 0) {
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteBegin: upload already active, path=%s\n",
                   upload_target_path_.c_str());
        return -RT_EBUSY;
    }

    // 先写临时文件，只有 fileWriteEnd() 校验完整大小后才发布到目标路径。
    (void)::unlink(temp_path.c_str());
    fd = ::open(temp_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
    if (fd < 0) {
        const int32_t err = posixErrorToRtError();
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteBegin: open failed, path=%s err=%d\n",
                   temp_path.c_str(),
                   (int)err);
        return err;
    }

    upload_fd_ = fd;
    upload_expected_size_ = total_size;
    upload_received_size_ = 0;
    upload_target_path_ = normalized_path;
    upload_temp_path_ = temp_path;
    rt_mutex_release(upload_mutex_);

    rt_kprintf("[FileService] fileWriteBegin: path=%s size=%u\n",
               upload_target_path_.c_str(),
               (unsigned)upload_expected_size_);
    return RT_EOK;
}

int32_t FileServiceImpl::fileWriteChunk(const binary_t* data)
{
    uint32_t remaining;
    const uint8_t* cursor;

    if (!upload_mutex_) {
        return -RT_ENOMEM;
    }

    if (!data || (!data->data && data->dataLength > 0)) {
        rt_kprintf("[FileService][E] fileWriteChunk: invalid chunk\n");
        return -RT_EINVAL;
    }

    rt_mutex_take(upload_mutex_, RT_WAITING_FOREVER);
    if (upload_fd_ < 0) {
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteChunk: upload not started\n");
        return -RT_ERROR;
    }

    // 防止客户端重传、偏移错误或长度错误导致写入超过声明的 total_size。
    if (data->dataLength > (upload_expected_size_ - upload_received_size_)) {
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteChunk: chunk overflow, recv=%u chunk=%u total=%u\n",
                   (unsigned)upload_received_size_,
                   (unsigned)data->dataLength,
                   (unsigned)upload_expected_size_);
        return -RT_EINVAL;
    }

    remaining = data->dataLength;
    cursor = data->data;
    // write() 可能只写入部分数据，因此循环直到当前 eRPC binary 块全部落盘。
    while (remaining > 0) {
        const ssize_t written = ::write(upload_fd_, cursor, remaining);
        if (written <= 0) {
            const int32_t err = posixErrorToRtError();
            (void)abortUploadLocked();
            rt_mutex_release(upload_mutex_);
            rt_kprintf("[FileService][E] fileWriteChunk: write failed, err=%d\n", (int)err);
            return err;
        }

        remaining -= (uint32_t)written;
        cursor += written;
        upload_received_size_ += (uint32_t)written;
    }

    rt_mutex_release(upload_mutex_);
    return RT_EOK;
}

int32_t FileServiceImpl::fileWriteEnd(void)
{
    if (!upload_mutex_) {
        return -RT_ENOMEM;
    }

    rt_mutex_take(upload_mutex_, RT_WAITING_FOREVER);
    if (upload_fd_ < 0) {
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteEnd: upload not started\n");
        return -RT_ERROR;
    }

    // 最终大小必须完全一致，避免缺块时发布不完整文件。
    if (upload_received_size_ != upload_expected_size_) {
        const uint32_t received = upload_received_size_;
        const uint32_t expected = upload_expected_size_;
        (void)abortUploadLocked();
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteEnd: size mismatch, recv=%u expected=%u\n",
                   (unsigned)received,
                   (unsigned)expected);
        return -RT_EINVAL;
    }

    if (::close(upload_fd_) != 0) {
        const int32_t err = posixErrorToRtError();
        upload_fd_ = -1;
        (void)::unlink(upload_temp_path_.c_str());
        resetUploadStateLocked();
        rt_mutex_release(upload_mutex_);
        rt_kprintf("[FileService][E] fileWriteEnd: close failed, err=%d\n", (int)err);
        return err;
    }
    upload_fd_ = -1;

    if (::rename(upload_temp_path_.c_str(), upload_target_path_.c_str()) != 0) {
        (void)::unlink(upload_target_path_.c_str());
        if (::rename(upload_temp_path_.c_str(), upload_target_path_.c_str()) != 0) {
            const int32_t err = posixErrorToRtError();
            (void)::unlink(upload_temp_path_.c_str());
            resetUploadStateLocked();
            rt_mutex_release(upload_mutex_);
            rt_kprintf("[FileService][E] fileWriteEnd: rename failed, err=%d\n", (int)err);
            return err;
        }
    }

    rt_kprintf("[FileService] fileWriteEnd: path=%s size=%u\n",
               upload_target_path_.c_str(),
               (unsigned)upload_received_size_);
    resetUploadStateLocked();
    rt_mutex_release(upload_mutex_);
    return RT_EOK;
}

int32_t FileServiceImpl::fileWriteAbort(void)
{
    int32_t err;

    if (!upload_mutex_) {
        return -RT_ENOMEM;
    }

    rt_mutex_take(upload_mutex_, RT_WAITING_FOREVER);
    err = abortUploadLocked();
    rt_mutex_release(upload_mutex_);
    return err;
}
