#pragma once

#include <array>
#include <string>
#include <filesystem>
#include <initializer_list>

#include <SDL3/SDL.h>

namespace AssetPath {

inline std::string& baseDirectory() {
    static std::string base;
    return base;
}

inline void setBasePath(std::string path) {
    baseDirectory() = std::move(path);
}

inline bool tryOpenFile(const std::string& candidate) {
    if (candidate.empty()) {
        return false;
    }
    SDL_IOStream* file = SDL_IOFromFile(candidate.c_str(), "rb");
    if (file) {
        SDL_CloseIO(file);
        return true;
    }
    return false;
}

inline bool fileExists(const std::string& path) {
    if (path.empty()) {
        return false;
    }

    if (tryOpenFile(path)) {
        return true;
    }

    std::filesystem::path p(path);
    if (!p.is_absolute()) {
        const std::string& base = baseDirectory();
        if (!base.empty()) {
            std::filesystem::path combined = std::filesystem::path(base) / p;
            if (tryOpenFile(combined.string())) {
                return true;
            }
        }
    }
    return false;
}

inline std::string resolveWithBase(const std::string& path) {
    if (path.empty()) {
        return path;
    }

    if (tryOpenFile(path)) {
        return path;
    }

    std::filesystem::path p(path);
    if (!p.is_absolute()) {
        const std::string& base = baseDirectory();
        if (!base.empty()) {
            std::filesystem::path combined = std::filesystem::path(base) / p;
            std::string combinedStr = combined.string();
            if (tryOpenFile(combinedStr)) {
                return combinedStr;
            }

#if defined(__APPLE__)
            // When running from a macOS bundle, SDL_GetBasePath may point at
            // Contents/Resources while assets are in Contents/MacOS. Search that
            // sibling too so Finder launches find the packaged assets.
            std::filesystem::path basePath(base);
            auto contentsDir = basePath.parent_path();
            if (contentsDir.filename() == "Resources") {
                auto macosDir = contentsDir.parent_path() / "MacOS";
                std::filesystem::path alt = macosDir / p;
                std::string altStr = alt.string();
                if (tryOpenFile(altStr)) {
                    return altStr;
                }
            }
#endif
        }
    }
    return path;
}

inline std::string resolveImagePath(const std::string& originalPath) {
    if (originalPath.empty()) {
        return originalPath;
    }

    if (auto resolved = resolveWithBase(originalPath); resolved != originalPath || fileExists(resolved)) {
        return resolved;
    }

    const std::size_t dot = originalPath.find_last_of('.');
    const std::string base = (dot == std::string::npos) ? originalPath : originalPath.substr(0, dot);

    static constexpr std::array<const char*, 5> preferredExtensions{
        ".jpg",
        ".jpeg",
        ".png",
        ".webp",
        ".bmp"
    };

    for (const char* ext : preferredExtensions) {
        std::string candidate = base + ext;
        if (candidate == originalPath) {
            continue;
        }
        std::string resolvedCandidate = resolveWithBase(candidate);
        if (resolvedCandidate != candidate || fileExists(resolvedCandidate)) {
            return resolvedCandidate;
        }
    }

    return originalPath;
}

inline std::string resolveWithExtensions(const std::string& basePathWithoutExt, std::initializer_list<const char*> extensions) {
    for (const char* ext : extensions) {
        std::string candidate = basePathWithoutExt + ext;
        std::string resolved = resolveWithBase(candidate);
        if (resolved != candidate || fileExists(resolved)) {
            return resolved;
        }
    }
    return {};
}

inline std::string resolveAudioPath(const std::string& basePathWithoutExt) {
    static constexpr std::array<const char*, 4> AUDIO_EXTENSIONS = {
        ".ogg",
        ".flac",
        ".wav",
        ".mp3"
    };
    for (const char* ext : AUDIO_EXTENSIONS) {
        std::string candidate = basePathWithoutExt + ext;
        std::string resolved = resolveWithBase(candidate);
        if (resolved != candidate || fileExists(resolved)) {
            return resolved;
        }
    }
    return {};
}

} // namespace AssetPath
