#include "app/BackgroundManager.h"
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include "utils/ImagePathResolver.h"

struct BackgroundManager::Impl {
    enum class Phase { Idle, ZoomOut, ZoomIn };
    SDL_Texture* currentTex = nullptr;
    SDL_Texture* nextTex = nullptr;
    int currentLevel = -1;
    int queuedLevel = -1;
    float phaseElapsedMs = 0.0f;
    float phaseDurationMs = 0.0f;
    float fadeDurationMs = 1200.0f;
    Phase phase = Phase::Idle;
};

static float getPhaseDurationMs(const BackgroundManager::Impl& fader, BackgroundManager::Impl::Phase ph) {
    const float total = std::max(1200.0f, fader.fadeDurationMs);
    switch (ph) {
        case BackgroundManager::Impl::Phase::ZoomOut: return total * 0.45f;
        case BackgroundManager::Impl::Phase::ZoomIn:  return total * 0.45f;
        default: return 0.0f;
    }
}

static void destroyTex(SDL_Texture*& t) {
    if (t) { SDL_DestroyTexture(t); t = nullptr; }
}

BackgroundManager::BackgroundManager() : impl(new Impl()) {}
BackgroundManager::~BackgroundManager() { reset(); delete impl; impl = nullptr; }

bool BackgroundManager::queueLevelBackground(SDL_Renderer* renderer, int level) {
    if (!renderer) return false;
    level = std::clamp(level, 0, 32);
    if (impl->currentLevel == level || impl->queuedLevel == level) return true;

    char bgPath[256];
    std::snprintf(bgPath, sizeof(bgPath), "assets/images/levels/level%d.jpg", level);
    const std::string resolved = AssetPath::resolveImagePath(bgPath);

    SDL_Surface* s = IMG_Load(resolved.c_str());
    if (!s) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Background load failed: %s (%s)", bgPath, resolved.c_str());
        return false;
    }
    SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, s);
    SDL_DestroySurface(s);
    if (!tex) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CreateTexture failed for %s", resolved.c_str());
        return false;
    }

    destroyTex(impl->nextTex);
    impl->nextTex = tex;
    impl->queuedLevel = level;

    if (!impl->currentTex) {
        impl->currentTex = impl->nextTex;
        impl->currentLevel = impl->queuedLevel;
        impl->nextTex = nullptr;
        impl->queuedLevel = -1;
        impl->phase = Impl::Phase::Idle;
        impl->phaseElapsedMs = 0.0f;
        impl->phaseDurationMs = 0.0f;
    } else if (impl->phase == Impl::Phase::Idle) {
        impl->phase = Impl::Phase::ZoomOut;
        impl->phaseDurationMs = getPhaseDurationMs(*impl, impl->phase);
        impl->phaseElapsedMs = 0.0f;
    }
    return true;
}

void BackgroundManager::update(float frameMs) {
    if (impl->phase == Impl::Phase::Idle) return;
    if (!impl->currentTex && !impl->nextTex) { impl->phase = Impl::Phase::Idle; return; }

    impl->phaseElapsedMs += frameMs;
    if (impl->phaseElapsedMs < std::max(1.0f, impl->phaseDurationMs)) return;

    if (impl->phase == Impl::Phase::ZoomOut) {
        if (impl->nextTex) {
            destroyTex(impl->currentTex);
            impl->currentTex = impl->nextTex;
            impl->currentLevel = impl->queuedLevel;
            impl->nextTex = nullptr;
            impl->queuedLevel = -1;
        }
        impl->phase = Impl::Phase::ZoomIn;
        impl->phaseDurationMs = getPhaseDurationMs(*impl, impl->phase);
        impl->phaseElapsedMs = 0.0f;
    } else if (impl->phase == Impl::Phase::ZoomIn) {
        impl->phase = Impl::Phase::Idle;
        impl->phaseElapsedMs = 0.0f;
        impl->phaseDurationMs = 0.0f;
    }
}

static void renderDynamic(SDL_Renderer* renderer, SDL_Texture* tex, int winW, int winH, float baseScale, float motionClockMs, float alphaMul) {
    if (!renderer || !tex) return;
    const float seconds = motionClockMs * 0.001f;
    const float wobble = std::max(0.4f, baseScale + std::sin(seconds * 0.07f) * 0.02f + std::sin(seconds * 0.23f) * 0.01f);
    const float rotation = std::sin(seconds * 0.035f) * 1.25f;
    const float panX = std::sin(seconds * 0.11f) * winW * 0.02f;
    const float panY = std::cos(seconds * 0.09f) * winH * 0.015f;
    SDL_FRect dest{ (winW - winW * wobble) * 0.5f + panX, (winH - winH * wobble) * 0.5f + panY, winW * wobble, winH * wobble };
    SDL_FPoint center{dest.w * 0.5f, dest.h * 0.5f};
    Uint8 alpha = static_cast<Uint8>(std::clamp(alphaMul, 0.0f, 1.0f) * 255.0f);
    SDL_SetTextureAlphaMod(tex, alpha);
    SDL_RenderTextureRotated(renderer, tex, nullptr, &dest, rotation, &center, SDL_FLIP_NONE);
    SDL_SetTextureAlphaMod(tex, 255);
}

void BackgroundManager::render(SDL_Renderer* renderer, int winW, int winH, float motionClockMs) {
    if (!renderer) return;
    SDL_FRect fullRect{0.f,0.f,(float)winW,(float)winH};
    float duration = std::max(1.0f, impl->phaseDurationMs);
    float progress = (impl->phase == Impl::Phase::Idle) ? 0.0f : std::clamp(impl->phaseElapsedMs / duration, 0.0f, 1.0f);
    const float seconds = motionClockMs * 0.001f;

    if (impl->phase == Impl::Phase::ZoomOut) {
        float scale = 1.0f + progress * 0.15f;
        if (impl->currentTex) {
            renderDynamic(renderer, impl->currentTex, winW, winH, scale, motionClockMs, (1.0f - progress * 0.4f));
            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(renderer, 0,0,0, Uint8(progress * 200.0f));
            SDL_RenderFillRect(renderer, &fullRect);
            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
        }
    } else if (impl->phase == Impl::Phase::ZoomIn) {
        float scale = 1.10f - progress * 0.10f;
        Uint8 alpha = Uint8((0.4f + progress * 0.6f) * 255.0f);
        if (impl->currentTex) {
            renderDynamic(renderer, impl->currentTex, winW, winH, scale, motionClockMs, alpha / 255.0f);
        }
    } else {
        if (impl->currentTex) {
            renderDynamic(renderer, impl->currentTex, winW, winH, 1.02f, motionClockMs, 1.0f);
            float pulse = 0.35f + 0.25f * (0.5f + 0.5f * std::sin(seconds * 0.5f));
            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(renderer, 5,12,28, Uint8(pulse * 90.0f));
            SDL_RenderFillRect(renderer, &fullRect);
            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
        } else if (impl->nextTex) {
            renderDynamic(renderer, impl->nextTex, winW, winH, 1.02f, motionClockMs, 1.0f);
        } else {
            SDL_SetRenderDrawColor(renderer, 0,0,0,255);
            SDL_RenderFillRect(renderer, &fullRect);
        }
    }
}

void BackgroundManager::reset() {
    destroyTex(impl->currentTex);
    destroyTex(impl->nextTex);
    impl->currentLevel = -1;
    impl->queuedLevel = -1;
    impl->phaseElapsedMs = 0.0f;
    impl->phaseDurationMs = 0.0f;
    impl->phase = Impl::Phase::Idle;
}
