import { Gpio } from "onoff";
import { exec } from "child_process";
import { config } from "dotenv";
import { io } from "socket.io-client";

config();

const KUMA_URL = process.env.WS_ENDPOINT || "wss://uptime.lea.pet";
const API_USER = process.env.API_USER!;
const API_PASS = process.env.API_PASS!;
const NO_GREEN = !!process.env.NO_GREEN;
const FUNNY = !process.env.NO_FUNNY; // why would you not want fart noises

if (!API_USER) throw new Error("Missing $API_USER");
if (!API_PASS) throw new Error("Missing $API_PASS");

const ledRed = new Gpio(Number(process.env.GPIO_RED || 14), "out");
const ledGreen = new Gpio(Number(process.env.GPIO_GREEN || 15), "out");

const monitors: { [key: string]: { status: boolean, index: number } } = {};
let paused: { [key: string]: boolean } = {};

const socket = io(`${KUMA_URL}`);

socket.on("monitorList", (items) => {
    console.log("got monitor list");

    paused = {};

    for (const item of Object.values(items) as any[]) {
        if (item.type == "group") continue;
        paused[item.id] = item.active == 0;
    }

    console.log(Object.keys(paused));

    think();
});

socket.on("importantHeartbeatList", (id, items) => {
    console.log("got heart beat list");

    for (const data of items) {
        const entry = monitors[id];
        const index = new Date(data.time).getTime();
        if (entry && entry.index >= index) continue;

        monitors[id] = {
            // status is 0 if down, 1 if up, 2 if paused. we only want to alert if it's 0
            status: !!data.status,
            index: index,
        }
    }

    think();
});


socket.on("heartbeat", (data) => {
    console.log("got heartbeat");

    const entry = monitors[''+data.monitorID];
    const index = new Date(data.time).getTime();
    if (entry && entry.index >= index) return;

    monitors[''+data.monitorID] = {
        // status is 0 if down, 1 if up, 2 if paused. we only want to alert if it's 0
        status: !!data.status,
        index: index,
    }

    think();
});

socket.on("connect", () => {
    console.log("Logging in");
    socket.emit("login", { username: API_USER, password: API_PASS, token: "" }, (res: any) => setStatus("green", false));
});

const sleep = (t: number) => new Promise(r => setTimeout(r, t));

type Status = "red" | "green" | "working";
let currentStatus: Status = "working";
function setStatus(status: Status, sound = true) {
    console.log("Status", status);
    currentStatus = status;
    switch(status) {
        case "green": {
            ledRed.writeSync(0);
            ledGreen.writeSync(NO_GREEN ? 0 : 1);

            if (sound && FUNNY) exec("aplay assets/yay.wav");
            break;
        }
        case "red": {
            ledRed.writeSync(1);
            ledGreen.writeSync(0);

            if (sound && FUNNY) exec("aplay assets/fart.wav");
            break;
        }
        case "working": {
            ledRed.writeSync(1);
            ledGreen.writeSync(1);
            break;
        }
    }
}

function think() {
    const down = Object.entries(monitors)
        .filter(m => !m[1].status && paused[m[0]] === false);

    if (down.length) {
        if (currentStatus != "red") setStatus("red");
    } else {
        if (currentStatus != "green") setStatus("green");
    }
}

setStatus("working");

setInterval(async () => {
    if (currentStatus == "green") {
        ledGreen.writeSync(NO_GREEN ? 1 : 0);
        await sleep(50);
        ledGreen.writeSync(NO_GREEN ? 0 : 1);
    }
    else if (currentStatus == "red") {
        for (let i = 0; i < 3; i++) {
            ledRed.writeSync(0);
            await sleep(50);
            ledRed.writeSync(1);
            await sleep(50);
        }

        // in case the status changed while we were flashing the LED
        if (currentStatus != "red") {
            setStatus(currentStatus);
        }
    }
}, 5000);
