#include <game/agame/background.hpp>
#include <game/agame/player.hpp>
#include <window/window.hpp>
#include <state/gamestate.hpp>
#include <object/camera.hpp>
#include <object/components/boxcollider.hpp>
#include <algorithm>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <utils.hpp>

namespace {
    void writeFinalScoreFile(int score) {
        std::ofstream file("score.txt", std::ios::trunc);
        if (!file.is_open()) {
            WARN("Neuspešno odpiranje score.txt za pisanje");
            return;
        }

        const auto now = std::chrono::system_clock::now();
        const std::time_t nowTime = std::chrono::system_clock::to_time_t(now);
        std::tm localTime{};
#if defined(_WIN32)
        localtime_s(&localTime, &nowTime);
#else
        localtime_r(&nowTime, &localTime);
#endif

        std::string playerName = Game::GameManager::getSharedData<std::string>("playerName");
        if (playerName.empty()) playerName = "Player";

        file << "Končna statistika igre:\n";
        file << "Igralec: " << playerName << "\n";
        file << "Točke: " << score << "\n";
        file << "Datum: " << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << "\n";
    }
}

namespace Game::AGame {
    void Background::start() {
        mSeaTex = std::make_shared<Game::Renderer::Texture>("../resources/l3sea.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "seaTex");
        mEnemyTex = std::make_shared<Game::Renderer::Texture>("../resources/l3enemy.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "enemyTex");
        mTrashTex = std::make_shared<Game::Renderer::Texture>("../resources/l3trash.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "trashTex");
        mFriendlyTex = std::make_shared<Game::Renderer::Texture>("../resources/l3friendly.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "friendlyTex");
        GameManager::setSharedData("enemyActiveCount", 0);
        GameManager::setSharedData("trashActiveCount", 0);
        GameManager::setSharedData("friendlyActiveCount", 0);
        GameManager::setSharedData("gameStage", 1);
        GameManager::setSharedData("gameWon", false);
        GameManager::setSharedData("gameLost", false);

        mZIndex = -1; // Ensure background renders behind other entities
        mTex->setTiled(true); // Set the background texture to be tiled
        if (mSeaTex) {
            mSeaTex->setTiled(true);
        }
        mTiledScale = 0.5f;
        SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &mW, &mH);

        // Land boundary: left 1/3 of map in centered coordinates
        // For 1280px window: -640 (left) + 426.67 (1/3) = -213.33
        mLandBoundaryX = -static_cast<float>(mW) / 2.f + static_cast<float>(mW) / 3.f;
        GameManager::setSharedData("terrainLandBoundaryX", mLandBoundaryX);
        GameManager::setSharedData("enemyRevealRadius", 260.f);

        mTransform.scaleX *= 10.f;
        mTransform.scaleY *= 10.f;

        mTransform.x -= mTex->getWidth() * mTransform.adjustedScaleX() / 2.f;
        mTransform.y -= mTex->getHeight() * mTransform.adjustedScaleY() / 2.f;

        LOG("W: " << mW << " H: " << mH);
        spawnLevel(1);
    }

    void Background::render(Game::Renderer::Renderer* renderer, Game::Renderer::RendererConfig config) {
        if (!renderer || !mTex || !mSeaTex) {
            return;
        }

        const float worldLeft = -static_cast<float>(mW) / 2.f;
        const float worldRight = static_cast<float>(mW) / 2.f;
        const float worldTop = -static_cast<float>(mH) / 2.f;
        const float worldBottom = static_cast<float>(mH) / 2.f;

        const float landLeft = worldLeft;
        const float landRight = mLandBoundaryX;
        const float seaLeft = mLandBoundaryX;
        const float seaRight = worldRight;

        auto drawSection = [&](const std::shared_ptr<Game::Renderer::Texture>& tex, float startX, float endX) {
            if (!tex) {
                return;
            }

            float tileW = 0.f;
            float tileH = 0.f;
            SDL_GetTextureSize(tex->getSDLTexture(), &tileW, &tileH);
            tileW *= mTiledScale;
            tileH *= mTiledScale;
            if (tileW <= 0.f || tileH <= 0.f) {
                return;
            }

            const float screenStartX = startX - config.camX + config.screenW / 2.f;
            const float screenEndX = endX - config.camX + config.screenW / 2.f;
            const float screenStartY = worldTop - config.camY + config.screenH / 2.f;
            const float screenEndY = worldBottom - config.camY + config.screenH / 2.f;

            for (float x = screenStartX; x < screenEndX; x += tileW) {
                for (float y = screenStartY; y < screenEndY; y += tileH) {
                    SDL_FRect dst{ x, y, tileW, tileH };
                    SDL_RenderTexture(renderer->getSDLRenderer(), tex->getSDLTexture(), nullptr, &dst);
                }
            }
        };

        drawSection(mTex, landLeft, landRight);
        drawSection(mSeaTex, seaLeft, seaRight);
    }

    void Background::spawnFriendly(int stage, int count) {
        const float viewLeft = -mW / 2.f;
        const float viewRight = mW / 2.f;
        const float viewTop = -mH / 2.f;
        const float viewBottom = mH / 2.f;

        Object::Transform tS;
        tS.rotation = 0.f;
        tS.scaleX = 6.f;
        tS.scaleY = 6.f;

        const float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f;
        const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f;

        // Split friendlies: most on land, a smaller number may appear on sea
        // Decide how many friendlies appear on the sea.
        // For stage 1 keep them on land; for later stages allow at least one on sea
        int seaCount = 0;
        if (stage > 1) {
            seaCount = std::max(1, count / 3); // roughly one third on sea for later stages
        }
        int landCount = count - seaCount;

        // Spawn land friendlies (left side)
        for (int i = 0; i < landCount; ++i) {
            tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewLeft + halfFriendlyW + 25.f), static_cast<int>(mLandBoundaryX - halfFriendlyW - 25.f)));
            tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfFriendlyH + 100.f), static_cast<int>(viewBottom - halfFriendlyH - 100.f)));
            auto* friendly = State::GameState::getInstance().addEntity(std::make_unique<AGame::Friendly>("Friendly" + std::to_string(stage) + "_L" + std::to_string(i + 1), mFriendlyTex, tS));
            if (friendly) {
                if (auto* collider = friendly->getComponent<Object::Components::BoxCollider>()) {
                    collider->setScale(0.75f);
                }
            }
        }

        // Spawn a smaller number of friendlies on the sea (right side)
        for (int i = 0; i < seaCount; ++i) {
            tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(mLandBoundaryX + halfFriendlyW + 25.f), static_cast<int>(viewRight - halfFriendlyW - 25.f)));
            tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfFriendlyH + 100.f), static_cast<int>(viewBottom - halfFriendlyH - 100.f)));
            auto* friendly = State::GameState::getInstance().addEntity(std::make_unique<AGame::Friendly>("Friendly" + std::to_string(stage) + "_S" + std::to_string(i + 1), mFriendlyTex, tS));
            if (friendly) {
                if (auto* collider = friendly->getComponent<Object::Components::BoxCollider>()) {
                    collider->setScale(0.75f);
                }
            }
        }
    }

    void Background::spawnLevel(int stage) {
        const float viewLeft = -mW / 2.f;
        const float viewRight = mW / 2.f;
        const float viewTop = -mH / 2.f;
        const float viewBottom = mH / 2.f;

        const int enemyCount = 2 + stage * 2;
        const int trashCount = 4 + stage * 3;
        const int friendlyCount = 1 + (stage - 1);

        GameManager::setSharedData("gameStage", stage);
        GameManager::setSharedData("enemyActiveCount", enemyCount);
        GameManager::setSharedData("trashActiveCount", trashCount);
        GameManager::setSharedData("friendlyActiveCount", friendlyCount);

        if (stage > 1) {
            auto* player = GameManager::getEntityByName<Player>("Player");
            if (player) {
                player->respawnRandomSea(mLandBoundaryX);
            }
        }

        Object::Transform tS;
        tS.rotation = 0.f;

        tS.scaleX = 7.f;
        tS.scaleY = 7.f;
        const float halfEnemyW = mEnemyTex->getWidth() * tS.adjustedScaleX() / 2.f;
        const float halfEnemyH = mEnemyTex->getHeight() * tS.adjustedScaleY() / 2.f;
        for (int i = 0; i < enemyCount; ++i) {
            tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewLeft + halfEnemyW + 25.f), static_cast<int>(mLandBoundaryX - halfEnemyW - 25.f)));
            tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfEnemyH + 100.f), static_cast<int>(viewBottom - halfEnemyH - 100.f)));
            GameManager::instantiateEntity(std::make_unique<AGame::Enemy>("Enemy" + std::to_string(stage) + "_" + std::to_string(i + 1), mEnemyTex, tS));
        }

        tS.scaleX = 5.5f;
        tS.scaleY = 5.5f;
        const float halfTrashW = mTrashTex->getWidth() * tS.adjustedScaleX() / 2.f;
        const float halfTrashH = mTrashTex->getHeight() * tS.adjustedScaleY() / 2.f;
        for (int i = 0; i < trashCount; ++i) {
            tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(mLandBoundaryX + halfTrashW + 25.f), static_cast<int>(viewRight - halfTrashW - 25.f)));
            tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfTrashH + 100.f), static_cast<int>(viewBottom - halfTrashH - 100.f)));
            GameManager::instantiateEntity(std::make_unique<AGame::Trash>("Trash" + std::to_string(stage) + "_" + std::to_string(i + 1), mTrashTex, tS));
        }

        spawnFriendly(stage, friendlyCount);
    }

    void Background::spawnTrashAt(const Object::Transform& tS, bool seaOnly) {
        // Prepare transform and ensure sea-only pop is placed on the sea side
        Object::Transform t = tS;
        t.rotation = 0.f;
        t.scaleX = 5.5f;
        t.scaleY = 5.5f;

        const float halfTrashW = mTrashTex->getWidth() * t.adjustedScaleX() / 2.f;

        if (seaOnly) {
            const float minSeaX = mLandBoundaryX + halfTrashW + 25.f;
            if (t.x < minSeaX) t.x = minSeaX;
        }

        // Create a unique-ish name for the auto-spawned trash
        const int id = Utils::getUtils().rirng32(0, 1000000);
        const std::string name = "Trash_Auto_" + std::to_string(id);
        GameManager::instantiateEntity(std::make_unique<AGame::Trash>(name, mTrashTex, t));

        // If requested, mark the spawned trash as sea-only
        if (seaOnly) {
            auto* tr = GameManager::getEntityByName<AGame::Trash>(name);
            if (tr) tr->setSeaOnly(true);
        }

        GameManager::setSharedData("trashActiveCount", GameManager::getSharedData<int>("trashActiveCount") + 1);
    }

    void Background::update(float deltaTime) {
        (void)deltaTime;

        const int enemyCount = GameManager::getSharedData<int>("enemyActiveCount");
        const int trashCount = GameManager::getSharedData<int>("trashActiveCount");
        const int stage = GameManager::getSharedData<int>("gameStage");

        // Periodically spawn a friendly on land or sea with a small probability
        // evaluated each update using deltaTime so the average interval is respected.
        const int activeFriendlies = GameManager::getSharedData<int>("friendlyActiveCount");
        if (activeFriendlies < mMaxAutoFriendlies) {
            // Compute chance = deltaTime / avgInterval
            const float chance = deltaTime / std::max(0.0001f, mFriendlySpawnAvgInterval);
            const int thresh = static_cast<int>(chance * 10000.f);
            if (thresh > 0) {
                const int roll = Utils::getUtils().rirng32(0, 9999);
                if (roll < thresh) {
                    // Decide side: sea probability increases with stage
                    float seaProb = (stage > 1) ? 0.3f : 0.1f;
                    const int sideRoll = Utils::getUtils().rirng32(0, 99);
                    const bool spawnSea = sideRoll < static_cast<int>(seaProb * 100.f);

                    Object::Transform tS;
                    tS.rotation = 0.f;
                    tS.scaleX = 6.f;
                    tS.scaleY = 6.f;
                    const float viewLeft = -mW / 2.f;
                    const float viewRight = mW / 2.f;
                    const float viewTop = -mH / 2.f;
                    const float viewBottom = mH / 2.f;
                    const float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f;
                    const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f;

                    if (!spawnSea) {
                        tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewLeft + halfFriendlyW + 25.f), static_cast<int>(mLandBoundaryX - halfFriendlyW - 25.f)));
                    } else {
                        tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(mLandBoundaryX + halfFriendlyW + 25.f), static_cast<int>(viewRight - halfFriendlyW - 25.f)));
                    }
                    tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfFriendlyH + 100.f), static_cast<int>(viewBottom - halfFriendlyH - 100.f)));

                    const int id = Utils::getUtils().rirng32(0, 1000000);
                    const std::string name = std::string("Friendly_Auto_") + std::to_string(id);
                    auto* friendly = State::GameState::getInstance().addEntity(std::make_unique<AGame::Friendly>(name, mFriendlyTex, tS));
                    if (friendly) {
                        if (auto* collider = friendly->getComponent<Object::Components::BoxCollider>()) {
                            collider->setScale(0.75f);
                        }
                        GameManager::setSharedData("friendlyActiveCount", GameManager::getSharedData<int>("friendlyActiveCount") + 1);
                    }
                }
            }
        }

        if (mPendingLevelSpawn) {
            if (enemyCount <= 0 && trashCount <= 0) {
                GameManager::processPendingEntityRemovals();
                mPendingLevelSpawn = false;
                spawnLevel(mPendingLevelStage);
            }
            return;
        }

        if (enemyCount <= 0 && trashCount <= 0) {
            if (stage < mMaxLevels) {
                mPendingLevelSpawn = true;
                mPendingLevelStage = stage + 1;
            } else if (!GameManager::getSharedData<bool>("gameWon")) {
                writeFinalScoreFile(GameManager::getSharedData<int>("gameScore"));
                GameManager::setSharedData("gameWon", true);
                LOG("Vsi nivoji so zaključeni");
            }
        }
    }

    void Background::onWindowResized(int newWidth, int newHeight) {
        mW = newWidth;
        mH = newHeight;
    }
}