// D:\Dev\Mosis\MosisService\designer\src\testing\action_player.cpp
#include "action_player.h"
#include "action_recorder.h"
#include <iostream>
#include <thread>

namespace mosis::testing {

ActionPlayer::ActionPlayer(Rml::Context* context)
    : m_context(context)
{
}

bool ActionPlayer::LoadSequence(const ActionSequence& sequence) {
    m_sequence = sequence;
    m_current_index = 0;
    m_finished = false;
    return true;
}

bool ActionPlayer::LoadFromFile(const std::string& path) {
    m_sequence = ActionRecorder::LoadFromFile(path);
    m_current_index = 0;
    m_finished = false;
    return !m_sequence.actions.empty();
}

void ActionPlayer::Start() {
    if (m_sequence.actions.empty()) return;
    
    // Navigate to initial screen if specified
    if (!m_sequence.initial_screen.empty() && m_navigate_cb) {
        m_navigate_cb(m_sequence.initial_screen);
    }
    
    m_start_time = std::chrono::steady_clock::now();
    m_paused_duration_ms = 0;
    m_min_finish_time_ms = 0;
    m_current_index = 0;
    m_playing = true;
    m_paused = false;
    m_finished = false;
    
    std::cout << "ActionPlayer: Started playback of " << m_sequence.actions.size() << " actions" << std::endl;
}

void ActionPlayer::Stop() {
    m_playing = false;
    m_paused = false;
    m_finished = true;
    std::cout << "ActionPlayer: Stopped" << std::endl;
}

void ActionPlayer::Pause() {
    if (m_playing && !m_paused) {
        m_paused = true;
        m_pause_time = std::chrono::steady_clock::now();
        std::cout << "ActionPlayer: Paused" << std::endl;
    }
}

void ActionPlayer::Resume() {
    if (m_playing && m_paused) {
        auto now = std::chrono::steady_clock::now();
        m_paused_duration_ms += std::chrono::duration_cast<std::chrono::milliseconds>(now - m_pause_time).count();
        m_paused = false;
        std::cout << "ActionPlayer: Resumed" << std::endl;
    }
}

float ActionPlayer::GetProgress() const {
    if (m_sequence.actions.empty()) return 1.0f;
    return static_cast<float>(m_current_index) / static_cast<float>(m_sequence.actions.size());
}

void ActionPlayer::Update() {
    if (!m_playing || m_paused || m_finished) return;

    auto now = std::chrono::steady_clock::now();
    int64_t elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start_time).count();
    elapsed_ms -= m_paused_duration_ms;

    // Check if all actions done AND minimum finish time reached
    if (m_current_index >= m_sequence.actions.size()) {
        if (elapsed_ms >= m_min_finish_time_ms) {
            m_finished = true;
            m_playing = false;
            std::cout << "ActionPlayer: Finished playback" << std::endl;
        }
        return;
    }

    // Execute all actions whose timestamp has passed
    while (m_current_index < m_sequence.actions.size()) {
        const auto& action = m_sequence.actions[m_current_index];
        int64_t action_time = GetActionTimestamp(action);

        if (action_time <= elapsed_ms) {
            ExecuteAction(action);
            m_current_index++;
        } else {
            break;
        }
    }
}

void ActionPlayer::ExecuteAction(const Action& action) {
    std::visit([this](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, TapAction>) {
            std::cout << "ActionPlayer: Tap at " << arg.x << ", " << arg.y << std::endl;
            SimulateTap(arg.x, arg.y);
        } else if constexpr (std::is_same_v<T, SwipeAction>) {
            std::cout << "ActionPlayer: Swipe from " << arg.x1 << "," << arg.y1 
                      << " to " << arg.x2 << "," << arg.y2 << std::endl;
            SimulateSwipe(arg.x1, arg.y1, arg.x2, arg.y2, arg.duration_ms);
        } else if constexpr (std::is_same_v<T, LongPressAction>) {
            std::cout << "ActionPlayer: Long press at " << arg.x << ", " << arg.y 
                      << " for " << arg.duration_ms << "ms" << std::endl;
            SimulateLongPress(arg.x, arg.y, arg.duration_ms);
        } else if constexpr (std::is_same_v<T, ButtonAction>) {
            std::cout << "ActionPlayer: Button " << arg.button << std::endl;
            SimulateButton(arg.button);
        } else if constexpr (std::is_same_v<T, WaitAction>) {
            std::cout << "ActionPlayer: Wait " << arg.duration_ms << "ms" << std::endl;
            // Extend the minimum finish time to allow the wait duration to elapse
            int64_t wait_end_time = arg.timestamp_ms + arg.duration_ms;
            if (wait_end_time > this->m_min_finish_time_ms) {
                this->m_min_finish_time_ms = wait_end_time;
            }
        } else if constexpr (std::is_same_v<T, KeyAction>) {
            std::cout << "ActionPlayer: Key " << arg.key_code << " " << (arg.pressed ? "down" : "up") << std::endl;
            if (arg.pressed) {
                m_context->ProcessKeyDown(static_cast<Rml::Input::KeyIdentifier>(arg.key_code), 0);
            } else {
                m_context->ProcessKeyUp(static_cast<Rml::Input::KeyIdentifier>(arg.key_code), 0);
            }
        }
    }, action);
}

void ActionPlayer::SimulateTap(int x, int y) {
    if (!m_context) return;
    m_context->ProcessMouseMove(x, y, 0);
    m_context->ProcessMouseButtonDown(0, 0);
    m_context->ProcessMouseButtonUp(0, 0);
}

void ActionPlayer::SimulateSwipe(int x1, int y1, int x2, int y2, int duration_ms) {
    if (!m_context) return;
    
    // Simulate swipe with intermediate points
    const int steps = 10;
    m_context->ProcessMouseMove(x1, y1, 0);
    m_context->ProcessMouseButtonDown(0, 0);
    
    for (int i = 1; i <= steps; i++) {
        float t = static_cast<float>(i) / static_cast<float>(steps);
        int x = x1 + static_cast<int>((x2 - x1) * t);
        int y = y1 + static_cast<int>((y2 - y1) * t);
        m_context->ProcessMouseMove(x, y, 0);
    }
    
    m_context->ProcessMouseButtonUp(0, 0);
}

void ActionPlayer::SimulateLongPress(int x, int y, int duration_ms) {
    if (!m_context) return;
    // For now, just simulate as a tap - RmlUi doesn't have built-in long press
    m_context->ProcessMouseMove(x, y, 0);
    m_context->ProcessMouseButtonDown(0, 0);
    // In real implementation, we'd delay the up event
    m_context->ProcessMouseButtonUp(0, 0);
}

void ActionPlayer::SimulateButton(const std::string& button) {
    if (button == "back") {
        m_context->ProcessKeyDown(Rml::Input::KI_ESCAPE, 0);
        m_context->ProcessKeyUp(Rml::Input::KI_ESCAPE, 0);
    } else if (button == "home" && m_navigate_cb) {
        m_navigate_cb("home");
    }
    // recents not implemented
}

} // namespace mosis::testing
