#include "microphone_interface.h"
#include <mosis/sandbox/permission_gate.h>
#include <lua.hpp>

namespace mosis {

// RecordingSession implementation

RecordingSession::RecordingSession(int id, const RecordingConfig& config)
    : m_id(id)
    , m_config(config)
    , m_active(true)
{
    // Initialize accumulated buffer with config
    m_accumulated.sample_rate = config.sample_rate;
    m_accumulated.channels = config.channels;
    m_accumulated.format = config.format;
    m_accumulated.sample_count = 0;
}

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

AudioBuffer RecordingSession::GetBuffer() {
    std::lock_guard<std::mutex> lock(m_mutex);

    // Return copy of accumulated buffer
    AudioBuffer buffer = m_accumulated;
    buffer.timestamp_ms = static_cast<uint64_t>(
        std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now().time_since_epoch()
        ).count()
    );

    return buffer;
}

void RecordingSession::Stop() {
    m_active = false;
}

void RecordingSession::SimulateBuffer(const AudioBuffer& buffer) {
    std::lock_guard<std::mutex> lock(m_mutex);

    if (!m_active) {
        return;
    }

    // Append to accumulated buffer
    m_accumulated.data.insert(
        m_accumulated.data.end(),
        buffer.data.begin(),
        buffer.data.end()
    );
    m_accumulated.sample_count += buffer.sample_count;

    if (m_on_buffer) {
        m_on_buffer(buffer);
    }
}

// MicrophoneInterface implementation

MicrophoneInterface::MicrophoneInterface(const std::string& app_id, PermissionGate* permissions)
    : m_app_id(app_id)
    , m_permissions(permissions)
    , m_mock_mode(true)
{
}

MicrophoneInterface::~MicrophoneInterface() {
    Shutdown();
}

void MicrophoneInterface::SimulateUserGesture() {
    m_last_gesture_time = std::chrono::steady_clock::now();
    m_has_gesture = true;
}

bool MicrophoneInterface::HasRecentUserGesture() const {
    if (!m_has_gesture) {
        return false;
    }

    auto now = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
        now - m_last_gesture_time
    ).count();

    return elapsed < GESTURE_VALIDITY_MS;
}

void MicrophoneInterface::ShowIndicator() {
    m_indicator_visible = true;
    // In real implementation: notify system UI to show recording indicator
}

void MicrophoneInterface::HideIndicator() {
    m_indicator_visible = false;
    // In real implementation: notify system UI to hide recording indicator
}

std::shared_ptr<RecordingSession> MicrophoneInterface::StartSession(const RecordingConfig& config, std::string& error) {
    std::lock_guard<std::mutex> lock(m_mutex);

    // Check permission
    if (m_permissions && !m_permissions->HasPermission("microphone")) {
        error = "Microphone permission not granted";
        return nullptr;
    }

    // Check user gesture
    if (!HasRecentUserGesture()) {
        error = "Microphone requires recent user gesture";
        return nullptr;
    }

    // Check for existing session
    if (m_active_session && m_active_session->IsActive()) {
        error = "Recording session already active";
        return nullptr;
    }

    // Validate sample rate
    int sample_rate = config.sample_rate;
    if (sample_rate != 8000 && sample_rate != 16000 &&
        sample_rate != 22050 && sample_rate != 44100 && sample_rate != 48000) {
        sample_rate = 44100;  // Default to 44100 if invalid
    }

    // Validate channels
    RecordingConfig validated_config = config;
    validated_config.sample_rate = sample_rate;
    if (validated_config.channels < 1) validated_config.channels = 1;
    if (validated_config.channels > 2) validated_config.channels = 2;

    // Create new session
    int id = m_next_session_id++;
    m_active_session = std::make_shared<RecordingSession>(id, validated_config);

    // Show recording indicator
    ShowIndicator();

    // In mock mode, session is created but no real microphone access
    // In real implementation: start audio recording hardware

    return m_active_session;
}

void MicrophoneInterface::StopSession() {
    std::lock_guard<std::mutex> lock(m_mutex);

    if (m_active_session) {
        m_active_session->Stop();
        m_active_session.reset();
        HideIndicator();
    }
}

bool MicrophoneInterface::HasActiveSession() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_active_session && m_active_session->IsActive();
}

bool MicrophoneInterface::IsIndicatorVisible() const {
    return m_indicator_visible;
}

void MicrophoneInterface::Shutdown() {
    StopSession();
}

// Lua API implementation

// Userdata for RecordingSession
struct LuaRecordingSession {
    std::weak_ptr<RecordingSession> session;
    int id;
};

static const char* RECORDING_SESSION_MT = "mosis.RecordingSession";

// Get MicrophoneInterface from upvalue
static MicrophoneInterface* GetMicrophoneInterface(lua_State* L) {
    return static_cast<MicrophoneInterface*>(lua_touserdata(L, lua_upvalueindex(1)));
}

// Get RecordingSession from userdata
static LuaRecordingSession* GetRecordingSession(lua_State* L, int index) {
    return static_cast<LuaRecordingSession*>(luaL_checkudata(L, index, RECORDING_SESSION_MT));
}

// session:getBuffer() -> buffer or nil
static int L_session_getBuffer(lua_State* L) {
    LuaRecordingSession* lrs = GetRecordingSession(L, 1);

    auto session = lrs->session.lock();
    if (!session || !session->IsActive()) {
        lua_pushnil(L);
        return 1;
    }

    AudioBuffer buffer = session->GetBuffer();

    // Return buffer as table
    lua_newtable(L);

    // buffer.sampleRate
    lua_pushinteger(L, buffer.sample_rate);
    lua_setfield(L, -2, "sampleRate");

    // buffer.channels
    lua_pushinteger(L, buffer.channels);
    lua_setfield(L, -2, "channels");

    // buffer.sampleCount
    lua_pushinteger(L, buffer.sample_count);
    lua_setfield(L, -2, "sampleCount");

    // buffer.timestamp
    lua_pushinteger(L, static_cast<lua_Integer>(buffer.timestamp_ms));
    lua_setfield(L, -2, "timestamp");

    // buffer.data (as string for binary data)
    if (!buffer.data.empty()) {
        lua_pushlstring(L, reinterpret_cast<const char*>(buffer.data.data()), buffer.data.size());
    } else {
        lua_pushstring(L, "");
    }
    lua_setfield(L, -2, "data");

    // buffer.format
    lua_pushstring(L, buffer.format == AudioFormat::PCM_FLOAT ? "float" : "pcm16");
    lua_setfield(L, -2, "format");

    return 1;
}

// session:stop()
static int L_session_stop(lua_State* L) {
    LuaRecordingSession* lrs = GetRecordingSession(L, 1);

    auto session = lrs->session.lock();
    if (session) {
        session->Stop();
    }

    // Also stop via interface to hide indicator
    MicrophoneInterface* mic = static_cast<MicrophoneInterface*>(
        lua_touserdata(L, lua_upvalueindex(1))
    );
    if (mic) {
        mic->StopSession();
    }

    return 0;
}

// session:on(event, callback)
static int L_session_on(lua_State* L) {
    LuaRecordingSession* lrs = GetRecordingSession(L, 1);

    auto session = lrs->session.lock();
    if (!session) {
        return 0;
    }

    const char* event = luaL_checkstring(L, 2);
    luaL_checktype(L, 3, LUA_TFUNCTION);

    // For now, simplified implementation that doesn't store callbacks
    // A full implementation would need to store refs and call them on events

    lua_pushboolean(L, 1);
    return 1;
}

// session:isActive() -> bool
static int L_session_isActive(lua_State* L) {
    LuaRecordingSession* lrs = GetRecordingSession(L, 1);

    auto session = lrs->session.lock();
    lua_pushboolean(L, session && session->IsActive() ? 1 : 0);
    return 1;
}

// RecordingSession garbage collection
static int L_session_gc(lua_State* L) {
    LuaRecordingSession* lrs = GetRecordingSession(L, 1);

    auto session = lrs->session.lock();
    if (session && session->IsActive()) {
        session->Stop();
    }

    return 0;
}

// microphone.start(config) -> session, error
static int L_microphone_start(lua_State* L) {
    MicrophoneInterface* mic = GetMicrophoneInterface(L);
    if (!mic) {
        lua_pushnil(L);
        lua_pushstring(L, "MicrophoneInterface not available");
        return 2;
    }

    RecordingConfig config;

    // Parse config table if provided
    if (lua_istable(L, 1)) {
        // sampleRate
        lua_getfield(L, 1, "sampleRate");
        if (lua_isnumber(L, -1)) {
            config.sample_rate = static_cast<int>(lua_tointeger(L, -1));
        }
        lua_pop(L, 1);

        // channels
        lua_getfield(L, 1, "channels");
        if (lua_isnumber(L, -1)) {
            config.channels = static_cast<int>(lua_tointeger(L, -1));
        }
        lua_pop(L, 1);

        // format
        lua_getfield(L, 1, "format");
        if (lua_isstring(L, -1)) {
            std::string fmt = lua_tostring(L, -1);
            if (fmt == "float" || fmt == "pcmfloat") {
                config.format = AudioFormat::PCM_FLOAT;
            } else {
                config.format = AudioFormat::PCM_16BIT;
            }
        }
        lua_pop(L, 1);
    }

    std::string error;
    auto session = mic->StartSession(config, error);

    if (!session) {
        lua_pushnil(L);
        lua_pushstring(L, error.c_str());
        return 2;
    }

    // Create userdata
    LuaRecordingSession* lrs = static_cast<LuaRecordingSession*>(
        lua_newuserdata(L, sizeof(LuaRecordingSession))
    );
    lrs->session = session;
    lrs->id = session->GetId();

    // Set metatable
    luaL_getmetatable(L, RECORDING_SESSION_MT);
    lua_setmetatable(L, -2);

    return 1;
}

// microphone.isActive() -> bool
static int L_microphone_isActive(lua_State* L) {
    MicrophoneInterface* mic = GetMicrophoneInterface(L);
    if (!mic) {
        lua_pushboolean(L, 0);
        return 1;
    }

    lua_pushboolean(L, mic->HasActiveSession() ? 1 : 0);
    return 1;
}

// microphone.stop()
static int L_microphone_stop(lua_State* L) {
    MicrophoneInterface* mic = GetMicrophoneInterface(L);
    if (mic) {
        mic->StopSession();
    }
    return 0;
}

// Helper to set a global in the real _G (bypassing any proxy)
static void SetGlobalInRealG(lua_State* L, const char* name) {
    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);

    if (lua_getmetatable(L, -1)) {
        lua_getfield(L, -1, "__index");
        if (lua_istable(L, -1)) {
            lua_pushvalue(L, -4);
            lua_setfield(L, -2, name);
            lua_pop(L, 4);
            return;
        }
        lua_pop(L, 2);
    }

    lua_pushvalue(L, -2);
    lua_setfield(L, -2, name);
    lua_pop(L, 2);
}

void RegisterMicrophoneAPI(lua_State* L, MicrophoneInterface* microphone) {
    // Create RecordingSession metatable
    luaL_newmetatable(L, RECORDING_SESSION_MT);

    lua_pushstring(L, "__index");
    lua_newtable(L);

    // Methods with microphone interface as upvalue for stop
    lua_pushlightuserdata(L, microphone);
    lua_pushcclosure(L, L_session_getBuffer, 1);
    lua_setfield(L, -2, "getBuffer");

    lua_pushlightuserdata(L, microphone);
    lua_pushcclosure(L, L_session_stop, 1);
    lua_setfield(L, -2, "stop");

    lua_pushcfunction(L, L_session_on);
    lua_setfield(L, -2, "on");

    lua_pushcfunction(L, L_session_isActive);
    lua_setfield(L, -2, "isActive");

    lua_settable(L, -3);  // Set __index

    // GC metamethod
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, L_session_gc);
    lua_settable(L, -3);

    lua_pop(L, 1);  // Pop metatable

    // Create microphone table
    lua_newtable(L);

    // microphone.start
    lua_pushlightuserdata(L, microphone);
    lua_pushcclosure(L, L_microphone_start, 1);
    lua_setfield(L, -2, "start");

    // microphone.isActive
    lua_pushlightuserdata(L, microphone);
    lua_pushcclosure(L, L_microphone_isActive, 1);
    lua_setfield(L, -2, "isActive");

    // microphone.stop
    lua_pushlightuserdata(L, microphone);
    lua_pushcclosure(L, L_microphone_stop, 1);
    lua_setfield(L, -2, "stop");

    // Set as global
    SetGlobalInRealG(L, "microphone");
}

} // namespace mosis
