#pragma once
#include "../db/pool.hpp"
#include "../log.hpp"
#include <string>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>

#include <vips/vips.h>

struct ImageResult {
    std::vector<uint8_t> buffer;
    std::string content_type;
};

static std::string content_type_for(const std::string& quelle) {
    size_t dot = quelle.rfind('.');
    std::string ext = (dot != std::string::npos) ? quelle.substr(dot + 1) : "";
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
    if (ext == "png") return "image/png";
    if (ext == "gif") return "image/gif";
    if (ext == "webp") return "image/webp";
    return "image/jpeg";
}

static std::string ext_for_content_type(const std::string& ct) {
    if (ct == "image/png") return ".png";
    if (ct == "image/gif") return ".gif";
    if (ct == "image/webp") return ".webp";
    return ".jpg";
}

static ImageResult resize_image(const uint8_t* data, size_t len,
                                int target_size, const std::string& content_type) {
    std::string fmt = ext_for_content_type(content_type);

    // Use vips_thumbnail_buffer for fast shrink-on-load
    VipsImage* out = nullptr;
    if (vips_thumbnail_buffer((void*)data, len, &out, target_size,
                              "height", target_size,
                              "no_rotate", TRUE,
                              nullptr)) {
        logc::warn("vips_thumbnail_buffer failed (%d): %s",
                   vips_error_buffer(), vips_error_buffer());
        vips_error_clear();
        return {};
    }

    // Write to memory buffer
    void* buf = nullptr;
    size_t buf_len = 0;
    if (vips_image_write_to_buffer(out, fmt.c_str(), &buf, &buf_len, nullptr)) {
        logc::warn("vips: failed to write resized image");
        g_object_unref(out);
        return {};
    }

    ImageResult result;
    result.buffer.assign((uint8_t*)buf, (uint8_t*)buf + buf_len);
    result.content_type = content_type;
    g_free(buf);
    g_object_unref(out);
    return result;
}

inline ImageResult get_image_by_hash(const std::string& hash, const std::string& size) {
    std::string sql =
        "SELECT bBild, bVorschauBild, nBreite, nHoehe, "
        "nVorschauBreite, nVorschauHoehe, cQuelle "
        "FROM dbo.tBild WHERE cHash = ?";
    Param p; p.type = ParamType::NVarChar; p.str_val = hash;
    ResultSet rs;
    bool ok = get_pool().execute(sql, {p}, rs);
    if (!ok) {
        logc::warn("image query failed for hash=%s", hash.c_str());
        return {};
    }
    if (rs.empty()) {
        logc::warn("image not found for hash=%s (query ok, 0 rows)", hash.c_str());
        return {};
    }

    auto& row = rs[0];

    int target = size.empty() ? 200 : std::stoi(size);
    std::string ct = content_type_for(row[6].str);
    int preview_w = row[4].str.empty() ? 0 : std::stoi(row[4].str);
    int preview_h = row[5].str.empty() ? 0 : std::stoi(row[5].str);
    int preview_max = std::max(preview_w, preview_h);

    bool has_full = !row[0].blob.empty();
    bool has_preview = !row[1].blob.empty();

    if (target <= 0) {
        if (!has_full) return {};
        ImageResult r;
        r.buffer = std::move(row[0].blob);
        r.content_type = ct;
        return r;
    }

    // If preview exists and target fits within preview, resize from preview
    if (has_preview && preview_max > 0 && target <= preview_max) {
        auto r = resize_image(row[1].blob.data(), row[1].blob.size(), target, ct);
        if (!r.buffer.empty()) return r;
        // Fallback: raw preview
        r.buffer = std::move(row[1].blob);
        r.content_type = ct;
        return r;
    }

    // Otherwise resize from full image
    if (has_full) {
        auto r = resize_image(row[0].blob.data(), row[0].blob.size(), target, ct);
        if (!r.buffer.empty()) return r;
        // Fallback: raw full
        r.buffer = std::move(row[0].blob);
        r.content_type = ct;
        return r;
    }

    // Last resort: raw preview
    if (has_preview) {
        ImageResult r;
        r.buffer = std::move(row[1].blob);
        r.content_type = ct;
        return r;
    }

    return {};
}
