#include "director.hpp"

#include <SDL3/SDL.h>
#include <sys/stat.h>

#include <cerrno>
#include <cstdlib>
#include <iostream>

#include "core/audio/audio.hpp"
#include "core/audio/audio_cache.hpp"
#include "core/defaults.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "game/escenes/escena_joc.hpp"
#include "game/escenes/escena_logo.hpp"
#include "game/escenes/escena_titol.hpp"
#include "game/options.hpp"
#include "context_escenes.hpp"
#include "project.h"

#ifndef _WIN32
#include <pwd.h>
#include <unistd.h>
#endif

// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;

// Constructor
Director::Director(std::vector<std::string> const& args) {
    std::cout << "Orni Attack - Inici\n";

    // Inicialitzar opcions amb valors per defecte
    Options::init();

    // Comprovar arguments del programa
    executable_path_ = checkProgramArguments(args);

    // Crear carpetes del sistema
    createSystemFolder("jailgames");
    createSystemFolder(std::string("jailgames/") + Project::NAME);

    // Establir ruta del fitxer de configuració
    Options::setConfigFile(system_folder_ + "/config.yaml");

    // Carregar o crear configuració
    Options::loadFromFile();

    if (Options::console) {
        std::cout << "Configuració carregada\n";
        std::cout << "  Finestra: " << Options::window.width << "×"
                  << Options::window.height << '\n';
        std::cout << "  Física: rotation=" << Options::physics.rotation_speed
                  << " rad/s\n";
    }

    std::cout << '\n';
}

Director::~Director() {
    // Guardar opcions
    Options::saveToFile();

    // Cleanup audio
    Audio::destroy();

    // Cleanup SDL
    SDL_Quit();

    std::cout << "\nAdéu!\n";
}

// Comprovar arguments del programa
auto Director::checkProgramArguments(std::vector<std::string> const& args)
    -> std::string {
    for (std::size_t i = 1; i < args.size(); ++i) {
        const std::string& argument = args[i];

        if (argument == "--console") {
            Options::console = true;
            std::cout << "Mode consola activat\n";
        } else if (argument == "--reset-config") {
            Options::init();
            Options::saveToFile();
            std::cout << "Configuració restablida als valors per defecte\n";
        }
    }

    return args[0];  // Retornar ruta de l'executable
}

// Crear carpeta del sistema (específic per plataforma)
void Director::createSystemFolder(const std::string& folder) {
#ifdef _WIN32
    system_folder_ = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__
    struct passwd* pw = getpwuid(getuid());
    const char* homedir = pw->pw_dir;
    system_folder_ =
        std::string(homedir) + "/Library/Application Support/" + folder;
#elif __linux__
    struct passwd* pw = getpwuid(getuid());
    const char* homedir = pw->pw_dir;
    system_folder_ = std::string(homedir) + "/.config/" + folder;

    // CRÍTIC: Crear ~/.config si no existeix
    {
        std::string config_base_folder = std::string(homedir) + "/.config";
        int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
        if (ret == -1 && errno != EEXIST) {
            printf("ERROR: No es pot crear la carpeta ~/.config\n");
            exit(EXIT_FAILURE);
        }
    }
#endif

    // Comprovar si la carpeta existeix
    struct stat st = {.st_dev = 0};
    if (stat(system_folder_.c_str(), &st) == -1) {
        errno = 0;

#ifdef _WIN32
        int ret = mkdir(system_folder_.c_str());
#else
        int ret = mkdir(system_folder_.c_str(), S_IRWXU);
#endif

        if (ret == -1) {
            switch (errno) {
                case EACCES:
                    printf("ERROR: Permisos denegats creant %s\n", system_folder_.c_str());
                    exit(EXIT_FAILURE);

                case EEXIST:
                    // La carpeta ja existeix (race condition), continuar
                    break;

                case ENAMETOOLONG:
                    printf("ERROR: Ruta massa llarga: %s\n", system_folder_.c_str());
                    exit(EXIT_FAILURE);

                default:
                    perror("mkdir");
                    exit(EXIT_FAILURE);
            }
        }
    }

    if (Options::console) {
        std::cout << "Carpeta del sistema: " << system_folder_ << '\n';
    }
}

// Bucle principal del joc
auto Director::run() -> int {
    // Calculate initial size from saved zoom_factor
    int initial_width = static_cast<int>(std::round(
        Defaults::Window::WIDTH * Options::window.zoom_factor));
    int initial_height = static_cast<int>(std::round(
        Defaults::Window::HEIGHT * Options::window.zoom_factor));

    // Crear gestor SDL amb configuració de Options
    SDLManager sdl(initial_width, initial_height, Options::window.fullscreen);

    // Inicialitzar sistema d'audio
    Audio::init();

    // Precachejar música per evitar lag al començar
    AudioCache::getMusic("title.ogg");
    if (Options::console) {
        std::cout << "Música precachejada: "
                  << AudioCache::getMusicCacheSize() << " fitxers\n";
    }

    // Crear context d'escenes
    ContextEscenes context;
    context.canviar_escena(Escena::LOGO);

    // Bucle principal de gestió d'escenes
    while (context.escena_desti() != Escena::EIXIR) {
        // Sincronitzar GestorEscenes::actual amb context
        // (altres sistemes encara poden llegir GestorEscenes::actual)
        GestorEscenes::actual = context.escena_desti();

        switch (context.escena_desti()) {
            case Escena::LOGO: {
                EscenaLogo logo(sdl, context);
                logo.executar();
                break;
            }

            case Escena::TITOL: {
                EscenaTitol titol(sdl, context);
                titol.executar();
                break;
            }

            case Escena::JOC: {
                EscenaJoc joc(sdl, context);
                joc.executar();
                break;
            }

            default:
                break;
        }
    }

    // Sincronitzar final amb GestorEscenes::actual
    GestorEscenes::actual = Escena::EIXIR;

    return 0;
}
