#!/usr/bin/python3
from __future__ import annotations

import json
import os
import subprocess
from collections import Counter
from dataclasses import dataclass
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

DAEMON_PORT = 23166
PCP_LOG_PATH = Path("/var/log/princh/pcp/pcp.log")

old_umask = os.umask(0)
PCP_LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
PCP_LOG_FILE = PCP_LOG_PATH.open("a")
os.umask(old_umask)


def log(level: str, message: str) -> None:
    try:
        now_str = (
            datetime.now(tz=timezone.utc)
            .isoformat(sep="T", timespec="milliseconds")
            .replace("+00:00", "")
            + "Z"
        )
        level_str = level.lower()[:4]
        print(
            f"{now_str} [{level_str}][daemon]: \t{message}",
            file=PCP_LOG_FILE,
            flush=True,
        )
    except OSError:
        pass


def get_gui_env_vars(user_id: int) -> dict[bytes, str]:
    keys = {b"DISPLAY", b"XAUTHORITY", b"WAYLAND_DISPLAY", b"DBUS_SESSION_BUS_ADDRESS"}
    counts: dict[bytes, Counter[str]] = {k: Counter() for k in keys}
    for process in Path("/proc").iterdir():
        environ_path = process / "environ"
        try:
            uid = environ_path.stat().st_uid
            if uid != user_id:
                continue
            env_kvs = environ_path.read_bytes().split(b"\0")
        except OSError:
            continue
        for entry in env_kvs:
            key, _, value = entry.partition(b"=")

            if key in keys:
                try:
                    str_value = str(value, "ascii")
                except ValueError:
                    continue

                counts[key][str_value] += 1

    env = {}
    for k, count in counts.items():
        most_common = count.most_common(1)
        if most_common:
            env[k] = most_common[0][0]

    return env


@dataclass
class Message:
    username: str
    user_id: int
    printer_id: str
    pdf_file_path: str
    title: str
    job_id: str
    options: str
    copies: str


class Handler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def do_POST(self) -> None:
        if self.path != "/":
            self.send_response(404)
            self.send_header("content-length", "0")
            self.end_headers()
            return

        try:
            length = int(self.headers.get("content-length", 0))
            data = self.rfile.read(length)
            args = Message(**json.loads(data))
            env = get_gui_env_vars(args.user_id)

            log("INFO", f"Found env: {env}")

            log("INFO", "Attempting to deliver print job.")

            subprocess.check_call(  # noqa: S603
                [  # noqa: S607
                    "sudo",
                    "-u",
                    args.username,
                    "ELECTRON_RUN_AS_NODE=1",
                    "--",
                    "/opt/princh/pcp/princh-cloud-printer",
                    "/opt/princh/pcp/client.js",
                    args.printer_id,
                    args.pdf_file_path,
                    args.username,
                    args.title,
                    args.job_id,
                    args.options,
                    args.copies,
                ],
                env=env,
            )

            self.send_response(200)
            self.send_header("content-length", "0")
            self.end_headers()
        except Exception as e:  # noqa: BLE001
            log("ERROR", str(e))
            self.send_response(500)
            self.send_header("content-length", "0")
            self.end_headers()


log("INFO", "Starting up...")

with ThreadingHTTPServer(("localhost", DAEMON_PORT), Handler) as server:
    log("INFO", "Started up")
    server.serve_forever()
