// update_service.cpp - Background app update checker implementation
// Milestone 10: Device-Side App Management

#include "update_service.h"
#include <mosis/util/logger.h>

#include <nlohmann/json.hpp>
#include <sstream>
#include <condition_variable>

using json = nlohmann::json;

namespace mosis {

UpdateService::UpdateService(AppManager* app_manager, const std::string& api_base)
    : m_app_manager(app_manager)
    , m_api_base(api_base)
{
    LOG_INFO("UpdateService initialized with API: %s", api_base.c_str());
}

UpdateService::~UpdateService() {
    Stop();
}

void UpdateService::Start(std::chrono::hours interval) {
    if (m_running) {
        LOG_WARN("UpdateService already running");
        return;
    }

    m_check_interval = interval;
    m_running = true;
    m_stop_requested = false;

    m_check_thread = std::thread(&UpdateService::CheckLoop, this);

    LOG_INFO("UpdateService started with %lld hour interval",
             static_cast<long long>(interval.count()));
}

void UpdateService::Stop() {
    if (!m_running) {
        return;
    }

    m_stop_requested = true;

    if (m_check_thread.joinable()) {
        m_check_thread.join();
    }

    m_running = false;
    LOG_INFO("UpdateService stopped");
}

std::vector<UpdateInfo> UpdateService::CheckForUpdates() {
    std::vector<UpdateInfo> updates;

    // Get list of installed apps
    auto installed = m_app_manager->GetInstalledApps();
    if (installed.empty()) {
        LOG_DEBUG("No apps installed, skipping update check");
        return updates;
    }

    // Build query string with package IDs and versions
    std::stringstream package_params;
    for (size_t i = 0; i < installed.size(); ++i) {
        if (i > 0) package_params << ",";
        package_params << installed[i].package_id << ":" << installed[i].version_code;
    }

    // Call API
    std::string url = m_api_base + "/store/apps/updates?packages=" + package_params.str();
    LOG_DEBUG("Checking for updates: %s", url.c_str());

    std::string response = HttpGet(url);
    if (response.empty()) {
        LOG_WARN("Update check failed: no response from server");
        return updates;
    }

    // Parse response
    try {
        json j = json::parse(response);

        if (j.contains("updates") && j["updates"].is_array()) {
            for (const auto& update_json : j["updates"]) {
                UpdateInfo info;
                info.package_id = update_json.value("package_id", "");
                info.name = update_json.value("name", "");
                info.new_version = update_json.value("version", "");
                info.new_version_code = update_json.value("version_code", 0);
                info.download_url = update_json.value("download_url", "");
                info.signature = update_json.value("signature", "");
                info.download_size = update_json.value("size", 0);
                info.release_notes = update_json.value("release_notes", "");
                info.is_critical = update_json.value("critical", false);

                // Get current version from installed apps
                auto current = m_app_manager->GetApp(info.package_id);
                if (current) {
                    info.current_version = current->version_name;
                    info.current_version_code = current->version_code;

                    // Only add if actually newer
                    if (info.new_version_code > info.current_version_code) {
                        updates.push_back(info);
                    }
                }
            }
        }

        LOG_INFO("Found %zu available updates", updates.size());

    } catch (const json::exception& e) {
        LOG_ERROR("Failed to parse update response: %s", e.what());
    }

    // Update last check time
    m_last_check = std::chrono::system_clock::now();

    // Store pending updates
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_pending_updates = updates;
    }

    return updates;
}

void UpdateService::CheckForUpdatesAsync(UpdateCheckCallback callback) {
    std::thread([this, callback]() {
        auto updates = CheckForUpdates();
        if (callback) {
            callback(updates);
        }
    }).detach();
}

bool UpdateService::ApplyUpdate(const std::string& package_id, ProgressCallback callback) {
    // Find update info
    UpdateInfo update_info;
    bool found = false;

    {
        std::lock_guard<std::mutex> lock(m_mutex);
        for (const auto& update : m_pending_updates) {
            if (update.package_id == package_id) {
                update_info = update;
                found = true;
                break;
            }
        }
    }

    if (!found) {
        LOG_ERROR("No pending update found for: %s", package_id.c_str());
        if (callback) {
            callback({InstallProgress::Stage::Failed, 0.0f, "No update available"});
        }
        return false;
    }

    LOG_INFO("Applying update: %s %s -> %s",
             package_id.c_str(),
             update_info.current_version.c_str(),
             update_info.new_version.c_str());

    // Use AppManager to download and install
    bool success = m_app_manager->Update(package_id,
                                         update_info.download_url,
                                         update_info.signature,
                                         callback);

    if (success) {
        // Remove from pending updates
        std::lock_guard<std::mutex> lock(m_mutex);
        m_pending_updates.erase(
            std::remove_if(m_pending_updates.begin(), m_pending_updates.end(),
                          [&](const UpdateInfo& u) { return u.package_id == package_id; }),
            m_pending_updates.end());
    }

    return success;
}

void UpdateService::ApplyUpdateAsync(const std::string& package_id,
                                     UpdateAppliedCallback callback) {
    std::thread([this, package_id, callback]() {
        bool success = ApplyUpdate(package_id, nullptr);
        if (callback) {
            callback(package_id, success);
        }
    }).detach();
}

void UpdateService::ApplyAllUpdates(UpdateAppliedCallback callback) {
    std::vector<UpdateInfo> updates;
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        updates = m_pending_updates;
    }

    for (const auto& update : updates) {
        bool success = ApplyUpdate(update.package_id, nullptr);
        if (callback) {
            callback(update.package_id, success);
        }
    }
}

std::vector<UpdateInfo> UpdateService::GetPendingUpdates() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_pending_updates;
}

bool UpdateService::HasPendingUpdates() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return !m_pending_updates.empty();
}

size_t UpdateService::GetPendingUpdateCount() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_pending_updates.size();
}

void UpdateService::CheckLoop() {
    LOG_DEBUG("UpdateService check loop started");

    // Wait for initial delay before first check
    auto wait_until = std::chrono::system_clock::now() + std::chrono::minutes(5);

    while (!m_stop_requested) {
        // Wait until next check time or stop requested
        auto now = std::chrono::system_clock::now();
        if (now < wait_until) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }

        // Check if on WiFi (if required)
        if (m_wifi_only && !IsOnWifi()) {
            LOG_DEBUG("Skipping update check: not on WiFi");
            wait_until = std::chrono::system_clock::now() + std::chrono::minutes(5);
            continue;
        }

        // Perform update check
        LOG_DEBUG("Performing scheduled update check");
        auto updates = CheckForUpdates();

        // Notify callback if updates available
        if (!updates.empty() && m_on_updates_available) {
            m_on_updates_available(updates);
        }

        // Auto-update if enabled
        if (m_auto_update && !updates.empty()) {
            if (!m_wifi_only || IsOnWifi()) {
                LOG_INFO("Auto-updating %zu apps", updates.size());
                ApplyAllUpdates(nullptr);
            }
        }

        // Schedule next check
        wait_until = std::chrono::system_clock::now() + m_check_interval;
    }

    LOG_DEBUG("UpdateService check loop ended");
}

std::string UpdateService::HttpGet(const std::string& url) {
    // TODO: Implement HTTP GET using platform-specific APIs
    // On Android, this would use JNI to call Java HttpURLConnection
    // For now, return empty string as placeholder
    LOG_WARN("HTTP GET not yet implemented for: %s", url.c_str());
    return "";
}

bool UpdateService::IsOnWifi() const {
    // TODO: Implement WiFi check using platform-specific APIs
    // For now, assume always on WiFi
    return true;
}

} // namespace mosis
