import argparse
import json
import os
from typing import Any, Dict
import requests

CONFIG_DIR = os.path.expanduser("~/.cxv")
CONFIG_PATH = os.path.join(CONFIG_DIR, "config.json")
DEFAULT_BASE_URL = "https://api.cxvre.com"


def _load_config() -> Dict[str, Any]:
    if not os.path.exists(CONFIG_PATH):
        return {}
    with open(CONFIG_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def _save_config(data: Dict[str, Any]) -> None:
    os.makedirs(CONFIG_DIR, exist_ok=True)
    with open(CONFIG_PATH, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)


def _session_from_config(cfg: Dict[str, Any]) -> requests.Session:
    sess = requests.Session()
    cookies = cfg.get("cookies", {})
    if isinstance(cookies, dict):
        sess.cookies.update(cookies)
    return sess


def _require_session(cfg: Dict[str, Any]) -> requests.Session:
    if not cfg.get("cookies"):
        raise SystemExit("Not logged in. Run: cxv login --email <email> --password <password>")
    return _session_from_config(cfg)


def cmd_login(args: argparse.Namespace) -> None:
    base_url = args.base_url or DEFAULT_BASE_URL
    sess = requests.Session()
    res = sess.post(
        f"{base_url}/auth/login",
        json={"email": args.email, "password": args.password},
        timeout=15,
    )
    if not res.ok:
        raise SystemExit(f"Login failed: {res.status_code} {res.text}")
    cfg = {
        "base_url": base_url,
        "cookies": requests.utils.dict_from_cookiejar(sess.cookies),
    }
    _save_config(cfg)
    print("Login OK")


def cmd_logout(_: argparse.Namespace) -> None:
    cfg = _load_config()
    if not cfg.get("cookies"):
        print("Already logged out")
        return
    sess = _session_from_config(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    try:
        sess.post(f"{base_url}/auth/logout", timeout=10)
    except Exception:
        pass
    _save_config({})
    print("Logged out")


def cmd_whoami(_: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/auth/me", timeout=15)
    if not res.ok:
        raise SystemExit(f"Auth check failed: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_list(_: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/projects", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to list projects: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_create(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/projects", json={"name": args.name}, timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to create project: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_get(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/projects/{args.project_id}", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to fetch project: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_archive(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/projects/{args.project_id}/archive", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to archive project: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_unarchive(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/projects/{args.project_id}/unarchive", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to unarchive project: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_api_keys_list(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/projects/{args.project_id}/api-keys", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to list API keys: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_api_keys_create(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/projects/{args.project_id}/api-keys", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to create API key: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_projects_api_keys_revoke(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.delete(f"{base_url}/projects/{args.project_id}/api-keys/{args.key_id}", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to revoke API key: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_list(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/projects/{args.project_id}/workflows", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to list workflows: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_publish(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/workflows/{args.workflow_id}/publish", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to publish workflow: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_deploy(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/workflows/{args.workflow_id}/deploy", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to deploy workflow: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_get(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/workflows/{args.workflow_id}", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to fetch workflow: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_run(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.post(f"{base_url}/workflows/{args.workflow_id}/run", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to run workflow: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_deployments(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/workflows/{args.workflow_id}/deployments", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to list deployments: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_workflows_requirements(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/workflows/{args.workflow_id}/requirements", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to fetch requirements: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_deployments_runs(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/deployments/{args.deployment_id}/runs", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to list runs: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def cmd_runs_logs(args: argparse.Namespace) -> None:
    cfg = _load_config()
    sess = _require_session(cfg)
    base_url = cfg.get("base_url", DEFAULT_BASE_URL)
    res = sess.get(f"{base_url}/runs/{args.run_id}/logs", timeout=15)
    if not res.ok:
        raise SystemExit(f"Failed to fetch logs: {res.status_code} {res.text}")
    print(json.dumps(res.json(), indent=2))


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="cxv")
    sub = parser.add_subparsers(dest="cmd", required=True)

    login = sub.add_parser("login")
    login.add_argument("--email", required=True)
    login.add_argument("--password", required=True)
    login.add_argument("--base-url")
    login.set_defaults(func=cmd_login)

    logout = sub.add_parser("logout")
    logout.set_defaults(func=cmd_logout)

    whoami = sub.add_parser("whoami")
    whoami.set_defaults(func=cmd_whoami)

    projects = sub.add_parser("projects")
    proj_sub = projects.add_subparsers(dest="subcmd", required=True)
    proj_list = proj_sub.add_parser("list")
    proj_list.set_defaults(func=cmd_projects_list)
    proj_create = proj_sub.add_parser("create")
    proj_create.add_argument("--name", required=True)
    proj_create.set_defaults(func=cmd_projects_create)
    proj_get = proj_sub.add_parser("get")
    proj_get.add_argument("--project-id", required=True)
    proj_get.set_defaults(func=cmd_projects_get)
    proj_archive = proj_sub.add_parser("archive")
    proj_archive.add_argument("--project-id", required=True)
    proj_archive.set_defaults(func=cmd_projects_archive)
    proj_unarchive = proj_sub.add_parser("unarchive")
    proj_unarchive.add_argument("--project-id", required=True)
    proj_unarchive.set_defaults(func=cmd_projects_unarchive)

    proj_keys = proj_sub.add_parser("api-keys")
    proj_keys_sub = proj_keys.add_subparsers(dest="keycmd", required=True)
    proj_keys_list = proj_keys_sub.add_parser("list")
    proj_keys_list.add_argument("--project-id", required=True)
    proj_keys_list.set_defaults(func=cmd_projects_api_keys_list)
    proj_keys_create = proj_keys_sub.add_parser("create")
    proj_keys_create.add_argument("--project-id", required=True)
    proj_keys_create.set_defaults(func=cmd_projects_api_keys_create)
    proj_keys_revoke = proj_keys_sub.add_parser("revoke")
    proj_keys_revoke.add_argument("--project-id", required=True)
    proj_keys_revoke.add_argument("--key-id", required=True)
    proj_keys_revoke.set_defaults(func=cmd_projects_api_keys_revoke)

    workflows = sub.add_parser("workflows")
    wf_sub = workflows.add_subparsers(dest="subcmd", required=True)
    wf_list = wf_sub.add_parser("list")
    wf_list.add_argument("--project-id", required=True)
    wf_list.set_defaults(func=cmd_workflows_list)
    wf_get = wf_sub.add_parser("get")
    wf_get.add_argument("--workflow-id", required=True)
    wf_get.set_defaults(func=cmd_workflows_get)
    wf_pub = wf_sub.add_parser("publish")
    wf_pub.add_argument("--workflow-id", required=True)
    wf_pub.set_defaults(func=cmd_workflows_publish)
    wf_dep = wf_sub.add_parser("deploy")
    wf_dep.add_argument("--workflow-id", required=True)
    wf_dep.set_defaults(func=cmd_workflows_deploy)
    wf_run = wf_sub.add_parser("run")
    wf_run.add_argument("--workflow-id", required=True)
    wf_run.set_defaults(func=cmd_workflows_run)
    wf_deployments = wf_sub.add_parser("deployments")
    wf_deployments.add_argument("--workflow-id", required=True)
    wf_deployments.set_defaults(func=cmd_workflows_deployments)
    wf_requirements = wf_sub.add_parser("requirements")
    wf_requirements.add_argument("--workflow-id", required=True)
    wf_requirements.set_defaults(func=cmd_workflows_requirements)

    deployments = sub.add_parser("deployments")
    dep_sub = deployments.add_subparsers(dest="subcmd", required=True)
    dep_runs = dep_sub.add_parser("runs")
    dep_runs.add_argument("--deployment-id", required=True)
    dep_runs.set_defaults(func=cmd_deployments_runs)

    runs = sub.add_parser("runs")
    runs_sub = runs.add_subparsers(dest="subcmd", required=True)
    runs_logs = runs_sub.add_parser("logs")
    runs_logs.add_argument("--run-id", required=True)
    runs_logs.set_defaults(func=cmd_runs_logs)

    return parser


def main() -> None:
    parser = build_parser()
    args = parser.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()
