// Copyright (c) 2026 Ourpad Network
// See LICENSE file in the project root for full license text.

#include "mixer/MixerControlServer.hpp"
#include "mixer/MixerEngine.hpp"
#include "mixer/MixerChannelStrip.hpp"
#include "mixer/MixerBus.hpp"

#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <cstdio>
#include <sstream>
#include <algorithm>
#include <nlohmann/json.hpp>

using namespace mixer;
using json = nlohmann::json;

MixerControlServer::MixerControlServer(MixerEngine* engine, const std::string& socketPath)
    : engine_(engine)
    , socketPath_(socketPath)
{
}

MixerControlServer::~MixerControlServer()
{
    stop();
}

bool MixerControlServer::start()
{
    // Remove existing socket file if present
    unlink(socketPath_.c_str());

    serverFd_ = socket(AF_UNIX, SOCK_STREAM, 0);
    if (serverFd_ < 0) {
        perror("socket");
        return false;
    }

    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socketPath_.c_str(), sizeof(addr.sun_path) - 1);

    if (bind(serverFd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(serverFd_);
        return false;
    }

    // Set directory permissions so any local process can connect
    chmod(socketPath_.c_str(), 0666);

    if (listen(serverFd_, 5) < 0) {
        perror("listen");
        close(serverFd_);
        return false;
    }

    running_ = true;
    serverThread_ = std::thread(&MixerControlServer::serverLoop, this);
    return true;
}

void MixerControlServer::stop()
{
    running_ = false;
    if (serverFd_ >= 0) {
        close(serverFd_);
        serverFd_ = -1;
    }
    if (serverThread_.joinable()) {
        serverThread_.join();
    }
    unlink(socketPath_.c_str());
}

void MixerControlServer::serverLoop()
{
    while (running_) {
        struct sockaddr_un clientAddr;
        socklen_t clientLen = sizeof(clientAddr);
        int clientFd = accept(serverFd_, (struct sockaddr*)&clientAddr, &clientLen);
        if (clientFd < 0) {
            if (running_) {
                perror("accept");
            }
            continue;
        }

        handleClient(clientFd);
        close(clientFd);
    }
}

void MixerControlServer::handleClient(int clientFd)
{
    char buffer[65536];
    ssize_t n = read(clientFd, buffer, sizeof(buffer) - 1);
    if (n <= 0) return;

    buffer[n] = '\0';

    // Process each line as a separate command
    std::string data(buffer);
    std::istringstream stream(data);
    std::string line;

    while (std::getline(stream, line)) {
        // Trim whitespace
        line.erase(line.begin(), std::find_if(line.begin(), line.end(),
            [](unsigned char c) { return !std::isspace(c); }));
        line.erase(std::find_if(line.rbegin(), line.rend(),
            [](unsigned char c) { return !std::isspace(c); }).base(), line.end());

        if (line.empty()) continue;

        std::string response = processCommand(line);
        response += '\n';

        write(clientFd, response.c_str(), response.size());
    }
}

std::string MixerControlServer::processCommand(const std::string& commandJson)
{
    try {
        json cmd = json::parse(commandJson);

        if (!cmd.contains("cmd")) {
            return json({{"status", "error"}, {"message", "missing 'cmd' field"}}).dump();
        }

        std::string command = cmd["cmd"];

        if (command == "getState") {
            return buildStateJson();
        }

        if (command == "getVU") {
            return buildVuJson();
        }

        if (command == "setChannelVolume") {
            int channel = cmd.value("channel", 0);
            float volume = cmd.value("volume", 0.0f);
            engine_->getChannel(channel)->setVolume(volume);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "setChannelPan") {
            int channel = cmd.value("channel", 0);
            float pan = cmd.value("pan", 0.0f);
            engine_->getChannel(channel)->setPan(pan);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "setChannelMute") {
            int channel = cmd.value("channel", 0);
            bool mute = cmd.value("mute", false);
            engine_->getChannel(channel)->setMute(mute);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "setChannelSolo") {
            int channel = cmd.value("channel", 0);
            bool solo = cmd.value("solo", false);
            engine_->getChannel(channel)->setSolo(solo);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "setChannelLabel") {
            int channel = cmd.value("channel", 0);
            std::string label = cmd.value("label", "");
            engine_->getChannel(channel)->setLabel(label);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "setBusVolume") {
            int64_t busId = cmd.value("busId", (int64_t)0);
            float volume = cmd.value("volume", 0.0f);
            auto* bus = engine_->getBus(busId);
            if (bus) bus->setVolume(volume);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "setBusMute") {
            int64_t busId = cmd.value("busId", (int64_t)0);
            bool mute = cmd.value("mute", false);
            auto* bus = engine_->getBus(busId);
            if (bus) bus->setMute(mute);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "routeChannelToBus") {
            int channel = cmd.value("channel", 0);
            int64_t busId = cmd.value("busId", (int64_t)0);
            float level = cmd.value("level", 0.0f);
            engine_->routeChannelToBus(channel, busId, level);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "routeBusToBus") {
            int64_t sourceBusId = cmd.value("sourceBusId", (int64_t)0);
            int64_t targetBusId = cmd.value("targetBusId", (int64_t)0);
            float level = cmd.value("level", 0.0f);
            engine_->routeBusToBus(sourceBusId, targetBusId, level);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "removeRoute") {
            int64_t sourceId = cmd.value("sourceId", (int64_t)0);
            int64_t targetBusId = cmd.value("targetBusId", (int64_t)0);
            engine_->removeRoute(sourceId, targetBusId);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "autoCreateChannels") {
            uint32_t inputs = cmd.value("inputs", (uint32_t)2);
            uint32_t outputs = cmd.value("outputs", (uint32_t)2);
            engine_->autoCreateChannels(inputs, outputs);
            return json({{"status", "ok"}}).dump();
        }

        if (command == "addChannel") {
            int physicalInput = cmd.value("physicalInput", 0);
            auto* channel = engine_->addChannel(physicalInput);
            int idx = channel ? channel->channelIndex() : -1;
            return json({{"status", "ok"}, {"channelIndex", idx}}).dump();
        }

        if (command == "removeChannel") {
            int channel = cmd.value("channel", 0);
            engine_->removeChannel(channel);
            return json({{"status", "ok"}}).dump();
        }

        return json({{"status", "error"}, {"message", "unknown command: " + command}}).dump();

    } catch (const json::parse_error& e) {
        return json({{"status", "error"}, {"message", std::string("JSON parse error: ") + e.what()}}).dump();
    } catch (const std::exception& e) {
        return json({{"status", "error"}, {"message", std::string("error: ") + e.what()}}).dump();
    }
}

std::string MixerControlServer::buildStateJson() const
{
    auto snapshot = engine_->captureSnapshot();

    json state;
    state["sampleRate"] = 48000;  // TODO: expose from engine

    // Encode channels
    json channels = json::array();
    for (const auto& cs : snapshot.channels) {
        json ch;
        ch["channelIndex"] = cs.channelIndex;
        ch["volume"] = cs.volume;
        ch["pan"] = cs.pan;
        ch["mute"] = cs.mute;
        ch["solo"] = cs.solo;
        ch["hpEnabled"] = cs.hpEnabled;
        ch["hpFrequency"] = cs.hpFrequency;
        ch["label"] = cs.label;

        const char* typeStr = "Instrument";
        switch (cs.channelType) {
            case MixerChannelType::Mic: typeStr = "Mic"; break;
            case MixerChannelType::Line: typeStr = "Line"; break;
            case MixerChannelType::AuxReturn: typeStr = "AuxReturn"; break;
            default: typeStr = "Instrument"; break;
        }
        ch["type"] = typeStr;

        json auxLevels = json::array();
        for (float level : cs.auxSendLevels) {
            auxLevels.push_back(level);
        }
        ch["auxSendLevels"] = auxLevels;

        channels.push_back(ch);
    }
    state["channels"] = channels;

    // Encode buses
    json buses = json::array();
    for (const auto& bs : snapshot.buses) {
        json bus;
        bus["id"] = bs.id;
        bus["name"] = bs.name;
        bus["volume"] = bs.volume;
        bus["mute"] = bs.mute;

        const char* typeStr = "Subgroup";
        switch (bs.type) {
            case MixerBusType::Master: typeStr = "Master"; break;
            case MixerBusType::Aux: typeStr = "Aux"; break;
            case MixerBusType::FxReturn: typeStr = "FxReturn"; break;
            default: typeStr = "Subgroup"; break;
        }
        bus["type"] = typeStr;

        buses.push_back(bus);
    }
    state["buses"] = buses;

    // Encode routes
    json routes = json::array();
    for (const auto& route : snapshot.routes) {
        json r;
        r["sourceId"] = route.sourceId;
        r["targetBusId"] = route.targetBusId;
        r["level"] = route.level;
        r["sourceType"] = (route.sourceType == MixerRouteEntry::SourceChannel) ? "channel" : "bus";
        routes.push_back(r);
    }
    state["routes"] = routes;

    // Output routes
    json outRoutes = json::array();
    for (const auto& route : engine_->outputRoutes()) {
        json r;
        r["sourceBusId"] = route.sourceBusId;
        r["sourceStartChannel"] = route.sourceStartChannel;
        r["targetStartChannel"] = route.targetStartChannel;
        r["channels"] = route.channels;
        outRoutes.push_back(r);
    }
    state["outputRoutes"] = outRoutes;

    // Physical I/O
    state["physicalInputCount"] = engine_->physicalInputCount();
    state["physicalOutputCount"] = engine_->physicalOutputCount();

    return state.dump();
}

std::string MixerControlServer::buildVuJson() const
{
    json vuReport;
    json channels = json::array();

    for (size_t i = 0; i < engine_->channelCount(); ++i) {
        auto* ch = engine_->getChannel(i);
        if (!ch) continue;
        json chVu;
        chVu["channelIndex"] = i;
        chVu["left"] = ch->vuLeft();
        chVu["right"] = ch->vuRight();
        channels.push_back(chVu);
    }
    vuReport["channels"] = channels;

    json buses = json::array();
    for (auto busId : engine_->busIds()) {
        auto* bus = engine_->getBus(busId);
        if (!bus) continue;
        json busVu;
        busVu["busId"] = busId;
        busVu["left"] = bus->vuLeft();
        busVu["right"] = bus->vuRight();
        buses.push_back(busVu);
    }
    vuReport["buses"] = buses;

    return vuReport.dump();
}
