#include "options.hpp"
#include "../core/defaults.hpp"
#include "../external/fkyaml_node.hpp"
#include "project.h"

#include <fstream>
#include <iostream>
#include <string>

namespace Options {

// Inicialitzar opcions amb valors per defecte de Defaults::
void init() {
#ifdef _DEBUG
  console = true;
#else
  console = false;
#endif

  // Window
  window.width = Defaults::Window::WIDTH;
  window.height = Defaults::Window::HEIGHT;
  window.fullscreen = false;
  window.size_increment = Defaults::Window::SIZE_INCREMENT;

  // Physics
  physics.rotation_speed = Defaults::Physics::ROTATION_SPEED;
  physics.acceleration = Defaults::Physics::ACCELERATION;
  physics.max_velocity = Defaults::Physics::MAX_VELOCITY;
  physics.friction = Defaults::Physics::FRICTION;
  physics.enemy_speed = Defaults::Physics::ENEMY_SPEED;
  physics.bullet_speed = Defaults::Physics::BULLET_SPEED;

  // Gameplay
  gameplay.max_enemies = Defaults::Entities::MAX_ORNIS;
  gameplay.max_bullets = Defaults::Entities::MAX_BALES;

  // Version
  version = std::string(Project::VERSION);
}

// Establir la ruta del fitxer de configuració
void setConfigFile(const std::string &path) { config_file_path = path; }

// Funcions auxiliars per carregar seccions del YAML

static void loadWindowConfigFromYaml(const fkyaml::node &yaml) {
  if (yaml.contains("window")) {
    const auto &win = yaml["window"];

    if (win.contains("width")) {
      try {
        auto val = win["width"].get_value<int>();
        window.width = (val >= Defaults::Window::MIN_WIDTH)
                           ? val
                           : Defaults::Window::WIDTH;
      } catch (...) {
        window.width = Defaults::Window::WIDTH;
      }
    }

    if (win.contains("height")) {
      try {
        auto val = win["height"].get_value<int>();
        window.height = (val >= Defaults::Window::MIN_HEIGHT)
                            ? val
                            : Defaults::Window::HEIGHT;
      } catch (...) {
        window.height = Defaults::Window::HEIGHT;
      }
    }

    if (win.contains("fullscreen")) {
      try {
        window.fullscreen = win["fullscreen"].get_value<bool>();
      } catch (...) {
        window.fullscreen = false;
      }
    }

    if (win.contains("size_increment")) {
      try {
        auto val = win["size_increment"].get_value<int>();
        window.size_increment =
            (val > 0) ? val : Defaults::Window::SIZE_INCREMENT;
      } catch (...) {
        window.size_increment = Defaults::Window::SIZE_INCREMENT;
      }
    }
  }
}

static void loadPhysicsConfigFromYaml(const fkyaml::node &yaml) {
  if (yaml.contains("physics")) {
    const auto &phys = yaml["physics"];

    if (phys.contains("rotation_speed")) {
      try {
        auto val = phys["rotation_speed"].get_value<float>();
        physics.rotation_speed =
            (val > 0) ? val : Defaults::Physics::ROTATION_SPEED;
      } catch (...) {
        physics.rotation_speed = Defaults::Physics::ROTATION_SPEED;
      }
    }

    if (phys.contains("acceleration")) {
      try {
        auto val = phys["acceleration"].get_value<float>();
        physics.acceleration =
            (val > 0) ? val : Defaults::Physics::ACCELERATION;
      } catch (...) {
        physics.acceleration = Defaults::Physics::ACCELERATION;
      }
    }

    if (phys.contains("max_velocity")) {
      try {
        auto val = phys["max_velocity"].get_value<float>();
        physics.max_velocity =
            (val > 0) ? val : Defaults::Physics::MAX_VELOCITY;
      } catch (...) {
        physics.max_velocity = Defaults::Physics::MAX_VELOCITY;
      }
    }

    if (phys.contains("friction")) {
      try {
        auto val = phys["friction"].get_value<float>();
        physics.friction = (val > 0) ? val : Defaults::Physics::FRICTION;
      } catch (...) {
        physics.friction = Defaults::Physics::FRICTION;
      }
    }

    if (phys.contains("enemy_speed")) {
      try {
        auto val = phys["enemy_speed"].get_value<float>();
        physics.enemy_speed = (val > 0) ? val : Defaults::Physics::ENEMY_SPEED;
      } catch (...) {
        physics.enemy_speed = Defaults::Physics::ENEMY_SPEED;
      }
    }

    if (phys.contains("bullet_speed")) {
      try {
        auto val = phys["bullet_speed"].get_value<float>();
        physics.bullet_speed =
            (val > 0) ? val : Defaults::Physics::BULLET_SPEED;
      } catch (...) {
        physics.bullet_speed = Defaults::Physics::BULLET_SPEED;
      }
    }
  }
}

static void loadGameplayConfigFromYaml(const fkyaml::node &yaml) {
  if (yaml.contains("gameplay")) {
    const auto &game = yaml["gameplay"];

    if (game.contains("max_enemies")) {
      try {
        auto val = game["max_enemies"].get_value<int>();
        gameplay.max_enemies =
            (val > 0 && val <= 50) ? val : Defaults::Entities::MAX_ORNIS;
      } catch (...) {
        gameplay.max_enemies = Defaults::Entities::MAX_ORNIS;
      }
    }

    if (game.contains("max_bullets")) {
      try {
        auto val = game["max_bullets"].get_value<int>();
        gameplay.max_bullets =
            (val > 0 && val <= 10) ? val : Defaults::Entities::MAX_BALES;
      } catch (...) {
        gameplay.max_bullets = Defaults::Entities::MAX_BALES;
      }
    }
  }
}

// Carregar configuració des del fitxer YAML
auto loadFromFile() -> bool {
  const std::string CONFIG_VERSION = std::string(Project::VERSION);

  std::ifstream file(config_file_path);
  if (!file.good()) {
    // El fitxer no existeix → crear-ne un de nou amb valors per defecte
    if (console) {
      std::cout << "Fitxer de config no trobat, creant-ne un de nou: "
                << config_file_path << '\n';
    }
    saveToFile();
    return true;
  }

  // Llegir tot el contingut del fitxer
  std::string content((std::istreambuf_iterator<char>(file)),
                      std::istreambuf_iterator<char>());
  file.close();

  try {
    // Parsejar YAML
    auto yaml = fkyaml::node::deserialize(content);

    // Validar versió
    if (yaml.contains("version")) {
      version = yaml["version"].get_value<std::string>();
    }

    if (CONFIG_VERSION != version) {
      // Versió incompatible → regenerar config
      if (console) {
        std::cout << "Versió de config incompatible (esperada: "
                  << CONFIG_VERSION << ", trobada: " << version
                  << "), regenerant config\n";
      }
      init();
      saveToFile();
      return true;
    }

    // Carregar seccions
    loadWindowConfigFromYaml(yaml);
    loadPhysicsConfigFromYaml(yaml);
    loadGameplayConfigFromYaml(yaml);

    if (console) {
      std::cout << "Config carregada correctament des de: " << config_file_path
                << '\n';
    }

    return true;

  } catch (const fkyaml::exception &e) {
    // Error de parsejat YAML → regenerar config
    if (console) {
      std::cerr << "Error parsejant YAML: " << e.what() << '\n';
      std::cerr << "Creant config nou amb valors per defecte\n";
    }
    init();
    saveToFile();
    return true;
  }
}

// Guardar configuració al fitxer YAML
auto saveToFile() -> bool {
  std::ofstream file(config_file_path);
  if (!file.is_open()) {
    if (console) {
      std::cerr << "No s'ha pogut obrir el fitxer de config per escriure: "
                << config_file_path << '\n';
    }
    return false;
  }

  // Escriure manualment per controlar format i comentaris
  file << "# Orni Attack - Fitxer de Configuració\n";
  file << "# Auto-generat. Les edicions manuals es preserven si són "
          "vàlides.\n\n";

  file << "version: \"" << Project::VERSION << "\"\n\n";

  file << "# FINESTRA\n";
  file << "window:\n";
  file << "  width: " << window.width << "\n";
  file << "  height: " << window.height << "\n";
  file << "  fullscreen: " << (window.fullscreen ? "true" : "false") << "\n";
  file << "  size_increment: " << window.size_increment << "\n\n";

  file << "# FÍSICA (tots els valors en px/s, rad/s, etc.)\n";
  file << "physics:\n";
  file << "  rotation_speed: " << physics.rotation_speed << "   # rad/s\n";
  file << "  acceleration: " << physics.acceleration << "     # px/s²\n";
  file << "  max_velocity: " << physics.max_velocity << "     # px/s\n";
  file << "  friction: " << physics.friction << "            # px/s²\n";
  file << "  enemy_speed: " << physics.enemy_speed
       << "       # unitats/frame\n";
  file << "  bullet_speed: " << physics.bullet_speed
       << "     # unitats/frame\n\n";

  file << "# GAMEPLAY\n";
  file << "gameplay:\n";
  file << "  max_enemies: " << gameplay.max_enemies << "\n";
  file << "  max_bullets: " << gameplay.max_bullets << "\n";

  file.close();

  if (console) {
    std::cout << "Config guardada a: " << config_file_path << '\n';
  }

  return true;
}

} // namespace Options
