# ==============================
# ======= HOSTISH AGENT =======
# ==============================

# ===== EDIT THESE 3 VALUES =====
USERNAME = "your_username_here"
PASSWORD = "your_password_here"
WEBSITE_DIR = "/path/to/your/website/folder"
# ================================

SERVER_URL = "wss://hostish.mekabrinevps.space/ws/host"

import asyncio
import base64
import json
import mimetypes
import os
import subprocess
import sys
import time


def ensure_websockets():
    try:
        import websockets  # noqa
        return
    except Exception:
        pass

    print("[hostish] Installing dependency: websockets...")
    try:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "websockets"])
    except Exception as e:
        print("[hostish] Failed to install websockets:", e)
        sys.exit(1)


ensure_websockets()
import websockets  # noqa: E402


def safe_join(root: str, rel: str) -> str:
    rel = (rel or "").lstrip("/").replace("\\", "/")
    parts = [p for p in rel.split("/") if p != ""]
    if any(p == ".." for p in parts):
        raise ValueError("invalid path")

    full = os.path.abspath(os.path.join(root, *parts))
    root_abs = os.path.abspath(root)

    if not (full == root_abs or full.startswith(root_abs + os.sep)):
        raise ValueError("path escapes root")

    return full


def read_file_bytes(path: str) -> bytes:
    with open(path, "rb") as f:
        return f.read()


async def handle_request(root_dir: str, req: dict) -> dict:
    req_id = req["id"]
    path = req.get("path", "") or ""

    if path.endswith("/"):
        path += "index.html"

    try:
        file_path = safe_join(root_dir, path)
    except ValueError:
        return {
            "type": "resp",
            "id": req_id,
            "status": 400,
            "headers": {"content-type": "text/plain; charset=utf-8"},
            "body_b64": base64.b64encode(b"Bad path.").decode("ascii"),
        }

    if not os.path.exists(file_path) or not os.path.isfile(file_path):
        return {
            "type": "resp",
            "id": req_id,
            "status": 404,
            "headers": {"content-type": "text/plain; charset=utf-8"},
            "body_b64": base64.b64encode(b"Not found.").decode("ascii"),
        }

    data = read_file_bytes(file_path)
    ct, _ = mimetypes.guess_type(file_path)

    return {
        "type": "resp",
        "id": req_id,
        "status": 200,
        "headers": {"content-type": ct or "application/octet-stream"},
        "body_b64": base64.b64encode(data).decode("ascii"),
    }


async def run_agent():
    root_dir = os.path.abspath(WEBSITE_DIR)

    print("[hostish] Starting agent")
    print("[hostish] Username:", USERNAME)
    print("[hostish] Root dir:", root_dir)

    if not os.path.isdir(root_dir):
        print("[hostish] ERROR: WEBSITE_DIR does not exist")
        return

    while True:
        try:
            async with websockets.connect(
                SERVER_URL,
                max_size=25 * 1024 * 1024,
                ping_interval=None,
            ) as ws:

                await ws.send(json.dumps({
                    "username": USERNAME,
                    "password": PASSWORD
                }))

                auth = json.loads(await ws.recv())
                if not auth.get("ok"):
                    print("[hostish] Authentication failed.")
                    return

                print("[hostish] Connected. Hosting is live.")
                last_ping = time.time()

                while True:
                    if time.time() - last_ping > 20:
                        await ws.send(json.dumps({"type": "pong"}))
                        last_ping = time.time()

                    msg = await asyncio.wait_for(ws.recv(), timeout=25)
                    req = json.loads(msg)

                    if req.get("type") != "req":
                        continue

                    resp = await handle_request(root_dir, req)
                    await ws.send(json.dumps(resp))

        except KeyboardInterrupt:
            print("\n[hostish] Exiting.")
            return
        except Exception as e:
            print("[hostish] Disconnected. Retrying in 2 seconds...", e)
            await asyncio.sleep(2)


if __name__ == "__main__":
    asyncio.run(run_agent())