from __future__ import annotations

import argparse
import json
import os
from pathlib import Path
from typing import Any

from .relay_registry import RelayRegistry


def _load_dotenv(path: Path) -> dict[str, str]:
    values: dict[str, str] = {}
    if not path.exists():
        return values
    for raw_line in path.read_text(encoding="utf-8").splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        key, value = line.split("=", 1)
        values[key.strip()] = value.strip().strip('"').strip("'")
    return values


def _env(name: str, env_file: dict[str, str], default: str | None = None) -> str | None:
    legacy = name.replace("CODEXMOBILE_", "CODEXBOT_")
    return os.environ.get(name) or os.environ.get(legacy) or env_file.get(name) or env_file.get(legacy) or default


def _registry(args: argparse.Namespace, env_file: dict[str, str]) -> RelayRegistry:
    db_path = args.db or _env("CODEXMOBILE_RELAY_DB_PATH", env_file)
    if not db_path:
        raise SystemExit("Missing relay DB path. Set CODEXMOBILE_RELAY_DB_PATH or pass --db.")
    return RelayRegistry(Path(db_path).expanduser())


def _next_port(registry: RelayRegistry, start: int = 18770) -> int:
    used = {device.tunnel_port for device in registry.list_devices()}
    port = start
    while port in used:
        port += 1
    return port


def _device_payload(device: Any, token: str | None = None) -> dict:
    payload = {
        "user": {
            "user_id": device.account.user_id,
            "email": device.account.email,
            "display_name": device.account.display_name,
            "plan": device.account.plan,
            "status": device.account.status,
            "expires_at": device.account.expires_at,
        },
        "device": {
            "device_id": device.device_id,
            "name": device.name,
            "mac_service_url": device.mac_service_url,
            "tunnel_port": device.tunnel_port,
            "status": device.status,
        },
    }
    if token:
        payload["mobile_token"] = token
    return payload


def init_owner(args: argparse.Namespace, env_file: dict[str, str]) -> None:
    registry = _registry(args, env_file)
    token = args.token or _env("CODEXMOBILE_APP_API_TOKEN", env_file)
    mac_url = args.mac_url or _env("CODEXMOBILE_MAC_SERVICE_URL", env_file)
    mac_token = args.mac_token or _env("CODEXMOBILE_MAC_SERVICE_TOKEN", env_file)
    if not token or not mac_url or not mac_token:
        raise SystemExit("Missing owner token/mac url/mac token. Use --token, --mac-url and --mac-token or .env values.")
    registry.ensure_owner_device(
        token=token,
        mac_service_url=mac_url,
        mac_service_token=mac_token,
        email=args.email or _env("CODEXMOBILE_RELAY_OWNER_EMAIL", env_file),
        display_name=args.display_name,
        device_name=args.device_name,
        tunnel_port=args.tunnel_port,
    )
    print(json.dumps({"ok": True, "owner": registry.describe_token(token)}, ensure_ascii=False, indent=2))


def create_user(args: argparse.Namespace, env_file: dict[str, str]) -> None:
    registry = _registry(args, env_file)
    tunnel_port = args.tunnel_port or _next_port(registry)
    mac_service_url = args.mac_url or f"http://127.0.0.1:{tunnel_port}"
    mac_service_token = args.mac_token
    if not mac_service_token:
        raise SystemExit("Missing --mac-token. Each user's Mac service token must be unique.")
    token, device = registry.create_user_with_device(
        email=args.email,
        display_name=args.display_name,
        device_name=args.device_name,
        mac_service_url=mac_service_url,
        mac_service_token=mac_service_token,
        tunnel_port=tunnel_port,
        plan=args.plan,
        expires_at=args.expires_at,
    )
    print(json.dumps(_device_payload(device, token=token), ensure_ascii=False, indent=2))


def list_devices(args: argparse.Namespace, env_file: dict[str, str]) -> None:
    registry = _registry(args, env_file)
    payload = [_device_payload(device) for device in registry.list_devices()]
    print(json.dumps({"devices": payload}, ensure_ascii=False, indent=2))


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Manage CodexMobile relay accounts and devices.")
    parser.add_argument("--env-file", default=".env", help="Path to the CodexMobile .env file.")
    parser.add_argument("--db", help="Relay SQLite DB path. Defaults to CODEXMOBILE_RELAY_DB_PATH.")
    subparsers = parser.add_subparsers(dest="command", required=True)

    owner = subparsers.add_parser("init-owner", help="Seed or refresh the single-owner relay account.")
    owner.add_argument("--token", help="Owner mobile API token.")
    owner.add_argument("--mac-url", help="Owner Mac service URL.")
    owner.add_argument("--mac-token", help="Owner Mac service token.")
    owner.add_argument("--email")
    owner.add_argument("--display-name", default="CodexMobile Owner")
    owner.add_argument("--device-name", default="Owner Mac")
    owner.add_argument("--tunnel-port", type=int, default=18765)
    owner.set_defaults(func=init_owner)

    create = subparsers.add_parser("create-user", help="Create a paid/free relay user and mobile token.")
    create.add_argument("--email")
    create.add_argument("--display-name")
    create.add_argument("--device-name", default="User Mac")
    create.add_argument("--mac-token", required=True, help="Token configured in that user's Mac menu client.")
    create.add_argument("--mac-url", help="Mac service URL. Defaults to http://127.0.0.1:<tunnel-port>.")
    create.add_argument("--tunnel-port", type=int, help="Reverse tunnel port on this VPS. Defaults to next free port.")
    create.add_argument("--plan", default="pro")
    create.add_argument("--expires-at", help="UTC ISO timestamp, for example 2026-05-15T00:00:00Z.")
    create.set_defaults(func=create_user)

    listing = subparsers.add_parser("list-devices", help="List relay devices.")
    listing.set_defaults(func=list_devices)

    return parser


def main() -> None:
    parser = build_parser()
    args = parser.parse_args()
    env_file = _load_dotenv(Path(args.env_file).expanduser())
    args.func(args, env_file)


if __name__ == "__main__":
    main()
