#include "quest/QuestJournal.h"
#include "external/nlohmann/json.hpp"
#include "utils/Utils.h"
#include <algorithm>
#include <iostream>

namespace ZL::Quest {

using json = nlohmann::json;

const char* toString(QuestStatus status) {
    switch (status) {
    case QuestStatus::Hidden: return "Hidden";
    case QuestStatus::Available: return "Available";
    case QuestStatus::Active: return "Active";
    case QuestStatus::Completed: return "Completed";
    case QuestStatus::Failed: return "Failed";
    default: return "Unknown";
    }
}

const char* toString(QuestCategory category) {
    switch (category) {
    case QuestCategory::Main: return "Main";
    case QuestCategory::Side: return "Side";
    case QuestCategory::Contract: return "Contract";
    case QuestCategory::Tutorial: return "Tutorial";
    default: return "Unknown";
    }
}

static QuestStatus parseQuestStatus(const std::string& value) {
    if (value == "Available") return QuestStatus::Available;
    if (value == "Active") return QuestStatus::Active;
    if (value == "Completed") return QuestStatus::Completed;
    if (value == "Failed") return QuestStatus::Failed;
    return QuestStatus::Hidden;
}

static QuestCategory parseQuestCategory(const std::string& value) {
    if (value == "Main") return QuestCategory::Main;
    if (value == "Contract") return QuestCategory::Contract;
    if (value == "Tutorial") return QuestCategory::Tutorial;
    return QuestCategory::Side;
}

bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipFile) {
    quests.clear();
    questOrder.clear();

    std::string content;
    try {
        if (zipFile.empty()) {
            content = ZL::readTextFile(path);
        }
        else {
            auto data = ZL::readFileFromZIP(path, zipFile);
            content.assign(data.begin(), data.end());
        }
    }
    catch (const std::exception& e) {
        std::cerr << "[quest] Failed to read " << path << ": " << e.what() << std::endl;
        return false;
    }

    if (content.empty()) {
        std::cerr << "[quest] Empty quest file: " << path << std::endl;
        return false;
    }

    json root;
    try {
        root = json::parse(content);
    }
    catch (const std::exception& e) {
        std::cerr << "[quest] JSON parse error in " << path << ": " << e.what() << std::endl;
        return false;
    }

    if (!root.contains("quests") || !root["quests"].is_array()) {
        std::cerr << "[quest] Missing quests array in " << path << std::endl;
        return false;
    }

    for (const auto& item : root["quests"]) {
        QuestDefinition def;
        def.id = item.value("id", "");
        def.title = item.value("title", def.id);
        def.description = item.value("description", "");
        def.category = parseQuestCategory(item.value("category", "Side"));
        def.initialStatus = parseQuestStatus(item.value("status", "Hidden"));
        def.recommendedLevel = item.value("recommendedLevel", 0);

        if (item.contains("objectives") && item["objectives"].is_array()) {
            for (const auto& obj : item["objectives"]) {
                QuestObjective objective;
                objective.id = obj.value("id", "");
                objective.text = obj.value("text", "");
                objective.completed = obj.value("completed", false);
                if (!objective.id.empty()) {
                    def.objectives.push_back(objective);
                }
            }
        }

        if (def.id.empty()) {
            continue;
        }

        QuestState state;
        state.definition = def;
        state.status = def.initialStatus;
        state.activeObjectiveIndex = 0;
        state.orderIndex = static_cast<int>(questOrder.size());

        quests[def.id] = std::move(state);
        questOrder.push_back(def.id);
    }

    return true;
}

bool QuestJournal::setStatus(const std::string& questId, QuestStatus status) {
    QuestState* quest = findQuest(questId);
    if (!quest) return false;
    quest->status = status;
    return true;
}

bool QuestJournal::unlockQuest(const std::string& questId) {
    QuestState* quest = findQuest(questId);
    if (!quest) return false;
    if (quest->status == QuestStatus::Hidden) {
        quest->status = QuestStatus::Available;
    }
    return true;
}

bool QuestJournal::startQuest(const std::string& questId) {
    return setStatus(questId, QuestStatus::Active);
}

bool QuestJournal::completeQuest(const std::string& questId) {
    return setStatus(questId, QuestStatus::Completed);
}

bool QuestJournal::failQuest(const std::string& questId) {
    return setStatus(questId, QuestStatus::Failed);
}

bool QuestJournal::setObjectiveCompleted(const std::string& questId, const std::string& objectiveId, bool completed) {
    QuestState* quest = findQuest(questId);
    if (!quest) return false;

    for (auto& objective : quest->definition.objectives) {
        if (objective.id == objectiveId) {
            objective.completed = completed;
            return true;
        }
    }

    return false;
}

bool QuestJournal::setActiveObjective(const std::string& questId, int objectiveIndex) {
    QuestState* quest = findQuest(questId);
    if (!quest) return false;

    if (objectiveIndex < 0 || objectiveIndex >= static_cast<int>(quest->definition.objectives.size())) {
        return false;
    }

    quest->activeObjectiveIndex = objectiveIndex;
    return true;
}

QuestState* QuestJournal::findQuest(const std::string& questId) {
    auto it = quests.find(questId);
    return (it != quests.end()) ? &it->second : nullptr;
}

const QuestState* QuestJournal::findQuest(const std::string& questId) const {
    auto it = quests.find(questId);
    return (it != quests.end()) ? &it->second : nullptr;
}

std::vector<QuestState*> QuestJournal::getVisibleQuests() {
    std::vector<QuestState*> result;
    for (const std::string& id : questOrder) {
        QuestState* quest = findQuest(id);
        if (!quest) continue;
        if (quest->status != QuestStatus::Hidden) {
            result.push_back(quest);
        }
    }
    return result;
}

std::vector<const QuestState*> QuestJournal::getVisibleQuests() const {
    std::vector<const QuestState*> result;
    for (const std::string& id : questOrder) {
        const QuestState* quest = findQuest(id);
        if (!quest) continue;
        if (quest->status != QuestStatus::Hidden) {
            result.push_back(quest);
        }
    }
    return result;
}

} // namespace ZL::Quest
