"""CLI: python -m cyclone users {create,list,disable,reset-password,set-role}.

Uses Click's CliRunner (matches the existing parse-837/parse-835 CLI tests).
The full CLI group lives in ``cyclone.cli.main`` — we exercise the
``users`` subgroup through the top-level ``main`` so the wiring is real.
"""

from __future__ import annotations

import pytest
from sqlalchemy import delete, select

from cyclone.auth import users
from cyclone.cli import main as cli_main
from cyclone.db import Session as DbSession
from cyclone.db import SessionLocal, User


@pytest.fixture(autouse=True)
def _clear():
    with SessionLocal()() as db:
        db.execute(delete(DbSession))
        db.execute(delete(User))
        db.commit()
    yield
    with SessionLocal()() as db:
        db.execute(delete(DbSession))
        db.execute(delete(User))
        db.commit()


def test_create_user_via_cli():
    from click.testing import CliRunner
    runner = CliRunner()
    result = runner.invoke(
        cli_main,
        [
            "users", "create", "cli-user",
            "--role", "viewer",
            "--password", "clipassword1",
        ],
        input="",  # don't prompt
    )
    assert result.exit_code == 0, result.output
    with SessionLocal()() as db:
        u = users.get_by_username(db, "cli-user")
    assert u is not None
    assert u.role == "viewer"


def test_list_users_via_cli():
    from click.testing import CliRunner
    with SessionLocal()() as db:
        users.create(db, username="listed", password="hunter2hunter2", role="user")
    runner = CliRunner()
    result = runner.invoke(cli_main, ["users", "list"])
    assert result.exit_code == 0, result.output
    assert "listed" in result.output


def test_disable_user_via_cli():
    from click.testing import CliRunner
    with SessionLocal()() as db:
        users.create(db, username="todie", password="hunter2hunter2", role="user")
    runner = CliRunner()
    result = runner.invoke(cli_main, ["users", "disable", "todie"])
    assert result.exit_code == 0, result.output
    with SessionLocal()() as db:
        u = users.get_by_username(db, "todie")
    assert u.disabled_at is not None


def test_reset_password_via_cli():
    from click.testing import CliRunner
    with SessionLocal()() as db:
        users.create(db, username="pwchange", password="oldpassword1", role="user")
    runner = CliRunner()
    result = runner.invoke(
        cli_main,
        [
            "users", "reset-password", "pwchange",
            "--password", "newpassword1",
        ],
    )
    assert result.exit_code == 0, result.output
    with SessionLocal()() as db:
        u = users.get_by_username(db, "pwchange")
    assert users.verify_password("newpassword1", u.password_hash)


def test_set_role_via_cli():
    from click.testing import CliRunner
    with SessionLocal()() as db:
        users.create(db, username="promote", password="hunter2hunter2", role="viewer")
    runner = CliRunner()
    result = runner.invoke(
        cli_main,
        ["users", "set-role", "promote", "--role", "admin"],
    )
    assert result.exit_code == 0, result.output
    with SessionLocal()() as db:
        u = users.get_by_username(db, "promote")
    assert u.role == "admin"


def test_create_rejects_short_password():
    from click.testing import CliRunner
    runner = CliRunner()
    result = runner.invoke(
        cli_main,
        [
            "users", "create", "weak",
            "--role", "viewer",
            "--password", "short",
        ],
    )
    # Click surfaces validation failures with a non-zero exit code.
    assert result.exit_code != 0, result.output
    with SessionLocal()() as db:
        rows = db.execute(select(User).where(User.username == "weak")).scalars().all()
    assert rows == []


def test_disable_unknown_user_exits_nonzero():
    from click.testing import CliRunner
    runner = CliRunner()
    result = runner.invoke(cli_main, ["users", "disable", "ghost"])
    assert result.exit_code != 0, result.output
