// D:\Dev\Mosis\MosisService\designer\src\testing\action_recorder.cpp
#include "action_recorder.h"
#include <nlohmann/json.hpp>
#include <fstream>
#include <cmath>

namespace mosis::testing {

using json = nlohmann::json;

ActionRecorder::ActionRecorder(int screen_width, int screen_height) {
    m_sequence.screen_width = screen_width;
    m_sequence.screen_height = screen_height;
}

void ActionRecorder::StartRecording(const std::string& initial_screen) {
    m_sequence.actions.clear();
    m_sequence.initial_screen = initial_screen;
    m_start_time = std::chrono::steady_clock::now();
    m_recording = true;
    m_mouse_down = false;
}

void ActionRecorder::StopRecording() {
    FinalizeCurrentAction();
    m_recording = false;
}

int64_t ActionRecorder::GetTimestamp() const {
    auto now = std::chrono::steady_clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start_time).count();
}

void ActionRecorder::RecordMouseDown(int x, int y) {
    if (!m_recording) return;
    
    m_mouse_down = true;
    m_mouse_down_x = x;
    m_mouse_down_y = y;
    m_mouse_down_time = GetTimestamp();
    m_last_x = x;
    m_last_y = y;
}

void ActionRecorder::RecordMouseUp(int x, int y) {
    if (!m_recording || !m_mouse_down) return;
    
    int64_t now = GetTimestamp();
    int64_t duration = now - m_mouse_down_time;
    int dx = x - m_mouse_down_x;
    int dy = y - m_mouse_down_y;
    int distance = static_cast<int>(std::sqrt(dx*dx + dy*dy));
    
    if (distance > m_swipe_threshold_pixels) {
        // Swipe
        SwipeAction swipe;
        swipe.x1 = m_mouse_down_x;
        swipe.y1 = m_mouse_down_y;
        swipe.x2 = x;
        swipe.y2 = y;
        swipe.duration_ms = static_cast<int>(duration);
        swipe.timestamp_ms = m_mouse_down_time;
        m_sequence.actions.push_back(swipe);
    } else if (duration > m_long_press_threshold_ms) {
        // Long press
        LongPressAction lp;
        lp.x = m_mouse_down_x;
        lp.y = m_mouse_down_y;
        lp.duration_ms = static_cast<int>(duration);
        lp.timestamp_ms = m_mouse_down_time;
        m_sequence.actions.push_back(lp);
    } else {
        // Tap
        TapAction tap;
        tap.x = m_mouse_down_x;
        tap.y = m_mouse_down_y;
        tap.timestamp_ms = m_mouse_down_time;
        m_sequence.actions.push_back(tap);
    }
    
    m_mouse_down = false;
}

void ActionRecorder::RecordMouseMove(int x, int y) {
    if (!m_recording) return;
    m_last_x = x;
    m_last_y = y;
}

void ActionRecorder::RecordButton(const std::string& button) {
    if (!m_recording) return;
    
    ButtonAction ba;
    ba.button = button;
    ba.timestamp_ms = GetTimestamp();
    m_sequence.actions.push_back(ba);
}

void ActionRecorder::RecordKey(int key_code, bool pressed) {
    if (!m_recording) return;
    
    KeyAction ka;
    ka.key_code = key_code;
    ka.pressed = pressed;
    ka.timestamp_ms = GetTimestamp();
    m_sequence.actions.push_back(ka);
}

void ActionRecorder::FinalizeCurrentAction() {
    if (m_mouse_down) {
        RecordMouseUp(m_last_x, m_last_y);
    }
}

// JSON serialization helpers
static json ActionToJson(const Action& action) {
    json j;
    std::visit([&j](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, TapAction>) {
            j["type"] = "tap";
            j["x"] = arg.x;
            j["y"] = arg.y;
            j["timestamp"] = arg.timestamp_ms;
        } else if constexpr (std::is_same_v<T, SwipeAction>) {
            j["type"] = "swipe";
            j["x1"] = arg.x1;
            j["y1"] = arg.y1;
            j["x2"] = arg.x2;
            j["y2"] = arg.y2;
            j["duration"] = arg.duration_ms;
            j["timestamp"] = arg.timestamp_ms;
        } else if constexpr (std::is_same_v<T, LongPressAction>) {
            j["type"] = "long_press";
            j["x"] = arg.x;
            j["y"] = arg.y;
            j["duration"] = arg.duration_ms;
            j["timestamp"] = arg.timestamp_ms;
        } else if constexpr (std::is_same_v<T, ButtonAction>) {
            j["type"] = "button";
            j["button"] = arg.button;
            j["timestamp"] = arg.timestamp_ms;
        } else if constexpr (std::is_same_v<T, WaitAction>) {
            j["type"] = "wait";
            j["duration"] = arg.duration_ms;
            j["timestamp"] = arg.timestamp_ms;
        } else if constexpr (std::is_same_v<T, KeyAction>) {
            j["type"] = "key";
            j["key_code"] = arg.key_code;
            j["pressed"] = arg.pressed;
            j["timestamp"] = arg.timestamp_ms;
        }
    }, action);
    return j;
}

static Action JsonToAction(const json& j) {
    std::string type = j["type"];
    
    if (type == "tap") {
        TapAction a;
        a.x = j["x"];
        a.y = j["y"];
        a.timestamp_ms = j["timestamp"];
        return a;
    } else if (type == "swipe") {
        SwipeAction a;
        a.x1 = j["x1"];
        a.y1 = j["y1"];
        a.x2 = j["x2"];
        a.y2 = j["y2"];
        a.duration_ms = j["duration"];
        a.timestamp_ms = j["timestamp"];
        return a;
    } else if (type == "long_press") {
        LongPressAction a;
        a.x = j["x"];
        a.y = j["y"];
        a.duration_ms = j["duration"];
        a.timestamp_ms = j["timestamp"];
        return a;
    } else if (type == "button") {
        ButtonAction a;
        a.button = j["button"];
        a.timestamp_ms = j["timestamp"];
        return a;
    } else if (type == "wait") {
        WaitAction a;
        a.duration_ms = j["duration"];
        a.timestamp_ms = j["timestamp"];
        return a;
    } else if (type == "key") {
        KeyAction a;
        a.key_code = j["key_code"];
        a.pressed = j["pressed"];
        a.timestamp_ms = j["timestamp"];
        return a;
    }
    
    // Default to wait action
    WaitAction w;
    w.duration_ms = 0;
    w.timestamp_ms = 0;
    return w;
}

bool ActionRecorder::SaveToFile(const std::string& path) const {
    json j;
    j["name"] = m_sequence.name;
    j["description"] = m_sequence.description;
    j["screen_width"] = m_sequence.screen_width;
    j["screen_height"] = m_sequence.screen_height;
    j["initial_screen"] = m_sequence.initial_screen;
    
    j["actions"] = json::array();
    for (const auto& action : m_sequence.actions) {
        j["actions"].push_back(ActionToJson(action));
    }
    
    std::ofstream file(path);
    if (!file) return false;
    file << j.dump(2);
    return true;
}

ActionSequence ActionRecorder::LoadFromFile(const std::string& path) {
    ActionSequence seq;
    
    std::ifstream file(path);
    if (!file) return seq;
    
    json j;
    file >> j;
    
    seq.name = j.value("name", "");
    seq.description = j.value("description", "");
    seq.screen_width = j.value("screen_width", 540);
    seq.screen_height = j.value("screen_height", 960);
    seq.initial_screen = j.value("initial_screen", "");
    
    if (j.contains("actions") && j["actions"].is_array()) {
        for (const auto& aj : j["actions"]) {
            seq.actions.push_back(JsonToAction(aj));
        }
    }
    
    return seq;
}

} // namespace mosis::testing
