import time
import json
import subprocess
from datetime import datetime
from pathlib import Path
import pulsectl
import psutil

from warehouse import Vpn_cache, Media_flag, http_get

class Cached:
    def __init__(self, ttl):
        self.ttl = ttl
        self.last = 0
        self.value = ""

    def get(self, fn):
        now = time.time()
        if now - self.last >= self.ttl:
            try:
                self.value = fn()
            except Exception:
                self.value = ""
            self.last = now
        return self.value


class StatusBar:
    def __init__(self):
        self.cpu = Cached(5)
        self.ram = Cached(5)
        self.bat = Cached(1)
        self.vpn = Cached(1)
        self.rec = Cached(1)

    def get_volume(self):
        with pulsectl.Pulse("statusbar") as pulse:
            sink = pulse.get_sink_by_name(
                pulse.server_info().default_sink_name
            )

            volume = sink.volume.value_flat * 100

            if sink.mute:
                return "Muted"

            return f"{volume:.0f}%"

    def get_ram(self):
        def compute():
            mem = psutil.virtual_memory()
            used = mem.total - mem.available

            return f"{used//1000**3}GB/{mem.total//1000**3}GB" # gib

        return self.ram.get(compute)

    def get_cpu(self):
        def compute():
            return f"{psutil.cpu_percent(interval=0.2):.1f}%"

        return self.cpu.get(compute)

    def get_battery(self):
        def compute():
            bat = psutil.sensors_battery()

            if bat is None:
                return ""

            if bat.power_plugged:
                status = "Charging"
            else:
                status = "Discharging"

            return f"{bat.percent:.0f}% ({status})"

        return self.bat.get(compute)

    def get_vpn(self):
        def compute():
            try:
                with open("/proc/net/dev") as f:
                    interfaces = [
                        line.split(":")[0].strip()
                        for line in f.readlines()[2:]
                    ]

                vpn = any(
                    iface.startswith(("wg", "tun", "tap", "ppp"))
                    for iface in interfaces
                )

            except Exception as e:
                return ""

            if not vpn:
                if Vpn_cache.exists():
                    Vpn_cache.unlink()
                return ""

            country = ""

            try:
                if Vpn_cache.exists():
                    country = Vpn_cache.read_text().strip()

                if not country:
                    ip = http_get("https://ifconfig.me")
                    geo = http_get(
                        f"https://iplookup.stab.ing/api/v1/lookup?ip={ip}"
                    )

                    country = json.loads(geo).get("country", "")

                    if country:
                        Vpn_cache.write_text(country)

            except Exception as e:
                print(e)

            return f"VPN: {country or 'NAY!'}"

        return self.vpn.get(compute)

    def get_recording(self):
        def compute():
            for proc in psutil.process_iter(["name"]):
                try:
                    if proc.info["name"] == "wf-recorder":
                        return "RECORDING!"
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    pass

            return ""

        return self.rec.get(compute)

    def get_time(self):
        if Media_flag.exists():
            return "0001-01-01 12:00 AM"

        return datetime.now().strftime("%Y-%m-%d %I:%M %p")

    def render(self):
        vol = self.get_volume()
        ram = self.get_ram()
        cpu = self.get_cpu()
        bat = self.get_battery()
        vpn = self.get_vpn()
        rec = self.get_recording()
        t = self.get_time()

        extra = " / ".join([x for x in [rec, vpn, bat] if x])

        if extra:
            return f"CPU: {cpu} / RAM: {ram} / VOL: {vol} / {extra} / {t}"

        return f"CPU: {cpu} / RAM: {ram} / VOL: {vol} / {t}"


def main():
    bar = StatusBar()

    while True:
        print(bar.render(), flush=True)


if __name__ == "__main__":
    main()