#include <frostbite2D/resource/audio_database.h>
#include <frostbite2D/resource/asset.h>
#include <rapidxml/rapidxml.hpp>
#include <SDL.h>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <ctime>
#include <numeric>
#include <random>

namespace frostbite2D {

AudioDatabase& AudioDatabase::get() {
  static AudioDatabase instance;
  return instance;
}

bool AudioDatabase::loadFromFile(const std::string& path) {
  clear();

  Asset& asset = Asset::get();
  std::string content;
  
  if (!asset.readTextFile(path, content)) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 无法读取文件: %s", path.c_str());
    return false;
  }

  return loadFromString(content);
}

bool AudioDatabase::loadFromString(const std::string& xmlContent) {
  clear();

  if (xmlContent.empty()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: XML 内容为空");
    return false;
  }

  std::string mutableContent = xmlContent;

  rapidxml::xml_document<> doc;
  try {
    doc.parse<rapidxml::parse_default>(&mutableContent[0]);
  } catch (const rapidxml::parse_error& e) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: XML 解析失败: %s", e.what());
    return false;
  }

  rapidxml::xml_node<>* root = doc.first_node("AudioTagDatabase");
  if (!root) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 未找到 AudioTagDatabase 根元素");
    return false;
  }

  for (rapidxml::xml_node<>* node = root->first_node(); node; node = node->next_sibling()) {
    const char* name = node->name();

    if (strcmp(name, "EFFECT") == 0) {
      rapidxml::xml_attribute<>* idAttr = node->first_attribute("ID");
      rapidxml::xml_attribute<>* fileAttr = node->first_attribute("FILE");
      
      if (!idAttr || !fileAttr) {
        continue;
      }

      if (strlen(idAttr->value()) == 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 跳过无效的 EFFECT 条目（ID 为空）");
        continue;
      }

      if (strlen(fileAttr->value()) == 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 跳过 EFFECT 条目（FILE 为空）: %s", idAttr->value());
        continue;
      }

      AudioEffect effect;
      effect.id = idAttr->value();
      effect.file = fileAttr->value();
      
      rapidxml::xml_attribute<>* subgroupAttr = node->first_attribute("SUBGROUP");
      if (subgroupAttr) {
        effect.subgroup = subgroupAttr->value();
      }
      
      rapidxml::xml_attribute<>* loopDelayAttr = node->first_attribute("LOOP_DELAY");
      if (loopDelayAttr) {
        effect.loopDelay = std::stoi(loopDelayAttr->value());
      }
      
      rapidxml::xml_attribute<>* loopDelayRangeAttr = node->first_attribute("LOOP_DELAY_RANGE");
      if (loopDelayRangeAttr) {
        effect.loopDelayRange = std::stoi(loopDelayRangeAttr->value());
      }

      effectMap_[effect.id] = std::move(effect);
    } else if (strcmp(name, "MUSIC") == 0) {
      rapidxml::xml_attribute<>* idAttr = node->first_attribute("ID");
      rapidxml::xml_attribute<>* fileAttr = node->first_attribute("FILE");
      
      if (!idAttr || !fileAttr) {
        continue;
      }

      if (strlen(idAttr->value()) == 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 跳过无效的 MUSIC 条目（ID 为空）");
        continue;
      }

      if (strlen(fileAttr->value()) == 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 跳过 MUSIC 条目（FILE 为空）: %s", idAttr->value());
        continue;
      }

      AudioMusic music;
      music.id = idAttr->value();
      music.file = fileAttr->value();
      
      rapidxml::xml_attribute<>* loopDelayAttr = node->first_attribute("LOOP_DELAY");
      if (loopDelayAttr) {
        music.loopDelay = std::stoi(loopDelayAttr->value());
      }

      musicMap_[music.id] = std::move(music);
    } else if (strcmp(name, "RANDOM") == 0) {
      rapidxml::xml_attribute<>* idAttr = node->first_attribute("ID");
      
      if (!idAttr) {
        continue;
      }

      if (strlen(idAttr->value()) == 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 跳过无效的 RANDOM 条目（ID 为空）");
        continue;
      }

      AudioRandom random;
      random.id = idAttr->value();

      for (rapidxml::xml_node<>* itemNode = node->first_node("ITEM"); itemNode; itemNode = itemNode->next_sibling("ITEM")) {
        rapidxml::xml_attribute<>* tagAttr = itemNode->first_attribute("TAG");
        rapidxml::xml_attribute<>* probAttr = itemNode->first_attribute("PROB");
        
        if (!tagAttr || !probAttr) {
          continue;
        }

        if (strlen(tagAttr->value()) == 0) {
          SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "AudioDatabase: 跳过 RANDOM 条目下的无效 ITEM（TAG 为空）: %s", random.id.c_str());
          continue;
        }

        RandomItem ri;
        ri.tag = tagAttr->value();
        ri.probability = std::stoi(probAttr->value());
        random.items.push_back(std::move(ri));
      }

      randomMap_[random.id] = std::move(random);
    }
  }

  loaded_ = true;
  
  SDL_Log("AudioDatabase: 加载完成 - %zu 个音效, %zu 个音乐, %zu 个随机组",
          effectMap_.size(), musicMap_.size(), randomMap_.size());

  return true;
}

void AudioDatabase::clear() {
  effectMap_.clear();
  musicMap_.clear();
  randomMap_.clear();
  loaded_ = false;
}

bool AudioDatabase::isLoaded() const {
  return loaded_;
}

std::optional<AudioEntry> AudioDatabase::get(const std::string& id) {
  if (!loaded_) {
    return std::nullopt;
  }

  AudioEntry entry;

  auto effectIt = effectMap_.find(id);
  if (effectIt != effectMap_.end()) {
    entry.type = AudioEntryType::Effect;
    entry.effect = &effectIt->second;
    return entry;
  }

  auto musicIt = musicMap_.find(id);
  if (musicIt != musicMap_.end()) {
    entry.type = AudioEntryType::Music;
    entry.music = &musicIt->second;
    return entry;
  }

  auto randomIt = randomMap_.find(id);
  if (randomIt != randomMap_.end()) {
    entry.type = AudioEntryType::Random;
    entry.random = &randomIt->second;
    return entry;
  }

  return std::nullopt;
}

std::optional<std::string> AudioDatabase::getSound(const std::string& id) {
  if (!loaded_) {
    return std::nullopt;
  }

  auto randomIt = randomMap_.find(id);
  if (randomIt != randomMap_.end()) {
    return selectFromRandom(randomIt->second);
  }

  if (effectMap_.find(id) != effectMap_.end()) {
    return id;
  }

  return std::nullopt;
}

std::optional<std::string> AudioDatabase::getFilePath(const std::string& id) {
  if (!loaded_) {
    return std::nullopt;
  }

  auto soundIdOpt = getSound(id);
  if (!soundIdOpt) {
    auto musicIt = musicMap_.find(id);
    if (musicIt != musicMap_.end()) {
      return musicIt->second.file;
    }
    return std::nullopt;
  }

  const std::string& soundId = *soundIdOpt;
  auto effectIt = effectMap_.find(soundId);
  if (effectIt != effectMap_.end()) {
    return effectIt->second.file;
  }

  return std::nullopt;
}

std::optional<const AudioEffect*> AudioDatabase::getEffect(const std::string& id) {
  if (!loaded_) {
    return std::nullopt;
  }

  auto it = effectMap_.find(id);
  if (it != effectMap_.end()) {
    return &it->second;
  }
  return std::nullopt;
}

std::optional<const AudioMusic*> AudioDatabase::getMusic(const std::string& id) {
  if (!loaded_) {
    return std::nullopt;
  }

  auto it = musicMap_.find(id);
  if (it != musicMap_.end()) {
    return &it->second;
  }
  return std::nullopt;
}

std::optional<const AudioRandom*> AudioDatabase::getRandom(const std::string& id) {
  if (!loaded_) {
    return std::nullopt;
  }

  auto it = randomMap_.find(id);
  if (it != randomMap_.end()) {
    return &it->second;
  }
  return std::nullopt;
}

size_t AudioDatabase::effectCount() const {
  return effectMap_.size();
}

size_t AudioDatabase::musicCount() const {
  return musicMap_.size();
}

size_t AudioDatabase::randomCount() const {
  return randomMap_.size();
}

void AudioDatabase::initializeRNG() {
  if (rngInitialized_) {
    return;
  }
  
  std::random_device rd;
  if (rd.entropy() > 0) {
    rng_.seed(rd());
  } else {
    auto now = std::chrono::high_resolution_clock::now();
    auto duration = now.time_since_epoch();
    auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    rng_.seed(static_cast<unsigned int>(millis));
  }
  
  rngInitialized_ = true;
  SDL_Log("AudioDatabase: 随机数生成器已初始化");
}

std::string AudioDatabase::selectFromRandom(const AudioRandom& randomGroup) {
  initializeRNG();
  
  std::vector<const RandomItem*> validItems;
  for (const auto& item : randomGroup.items) {
    if (!item.tag.empty()) {
      validItems.push_back(&item);
    }
  }
  
  if (validItems.empty()) {
    return "";
  }

  if (validItems.size() == 1) {
    return validItems[0]->tag;
  }

  int32 totalWeight = 0;
  for (const auto* item : validItems) {
    totalWeight += item->probability;
  }

  if (totalWeight <= 0) {
    std::uniform_int_distribution<size_t> dist(0, validItems.size() - 1);
    return validItems[dist(rng_)]->tag;
  }

  std::uniform_int_distribution<int32> dist(1, totalWeight);
  int32 randomValue = dist(rng_);

  int32 currentWeight = 0;
  for (const auto* item : validItems) {
    currentWeight += item->probability;
    if (randomValue <= currentWeight) {
      return item->tag;
    }
  }

  return validItems.back()->tag;
}

bool AudioDatabase::has(const std::string& id) {
  if (!loaded_) {
    return false;
  }
  return effectMap_.count(id) > 0 || musicMap_.count(id) > 0 || randomMap_.count(id) > 0;
}

std::string AudioDatabase::filePath(const std::string& id) {
  auto opt = getFilePath(id);
  return opt ? *opt : "";
}

std::string AudioDatabase::soundId(const std::string& idOrRandomId) {
  auto opt = getSound(idOrRandomId);
  return opt ? *opt : "";
}

std::string AudioDatabase::subgroup(const std::string& id) {
  if (!loaded_) {
    return "";
  }
  auto it = effectMap_.find(id);
  if (it != effectMap_.end()) {
    return it->second.subgroup;
  }
  return "";
}

AudioEntryType AudioDatabase::typeOf(const std::string& id) {
  if (!loaded_) {
    return AudioEntryType::None;
  }
  if (effectMap_.count(id) > 0) {
    return AudioEntryType::Effect;
  }
  if (musicMap_.count(id) > 0) {
    return AudioEntryType::Music;
  }
  if (randomMap_.count(id) > 0) {
    return AudioEntryType::Random;
  }
  return AudioEntryType::None;
}

bool AudioDatabase::tryGetFilePath(const std::string& id, std::string& outPath) {
  auto opt = getFilePath(id);
  if (opt) {
    outPath = *opt;
    return true;
  }
  return false;
}

bool AudioDatabase::tryGetSoundId(const std::string& id, std::string& outSoundId) {
  auto opt = getSound(id);
  if (opt) {
    outSoundId = *opt;
    return true;
  }
  return false;
}

} // namespace frostbite2D
