#!/usr/bin/env python3
"""
Password Hash Helper for copyparty

Usage:
    ./pw-helper.py <hostname> <username> <password>

Example:
    ./pw-helper.py docker-util01 kiney my_secure_password

This tool:
1. Reads the ah_salt from host_vars/<hostname>.yml
2. Generates an argon2id hash compatible with copyparty
3. Outputs the hash to stdout for use in Ansible config

Requirements:
    pip install argon2-cffi pyyaml
"""

import base64
import os
import sys
from pathlib import Path

try:
    import yaml
except ImportError:
    print("ERROR: PyYAML not found. Install with: pip install pyyaml", file=sys.stderr)
    sys.exit(1)

try:
    from argon2.low_level import Type as ArgonType
    from argon2.low_level import hash_secret
except ImportError:
    print(
        "ERROR: argon2-cffi not found. Install with: pip install argon2-cffi",
        file=sys.stderr,
    )
    sys.exit(1)


def error(msg):
    """Print error and exit"""
    print(f"ERROR: {msg}", file=sys.stderr)
    sys.exit(1)


def find_ansible_root():
    """Find the ansible root directory (contains host_vars/)"""
    current = Path(__file__).resolve().parent

    # Walk up from roles/docker_copyparty/
    while current.parent != current:
        if (current / "host_vars").exists():
            return current
        current = current.parent

    error("Could not find ansible root (host_vars/ directory)")


def get_ah_salt(hostname):
    """Read ah_salt from host_vars/<hostname>.yml"""
    ansible_root = find_ansible_root()
    host_vars = ansible_root / "host_vars" / f"{hostname}.yml"

    if not host_vars.exists():
        error(f"Host vars file not found: {host_vars}")

    try:
        with open(host_vars) as f:
            data = yaml.safe_load(f)
    except Exception as e:
        error(f"Failed to parse YAML from {host_vars}: {e}")

    # Get copyparty.ah_salt
    if "copyparty" not in data:
        error(f"No 'copyparty' section in {host_vars}")

    ah_salt = data["copyparty"].get("ah_salt", "")

    if not ah_salt:
        error(
            f"No 'ah_salt' configured for {hostname}\n"
            f"Add to {host_vars}:\n"
            f"  copyparty:\n"
            f'    ah_salt: "$(openssl rand -base64 32)"'
        )

    return ah_salt


def generate_copyparty_hash(plain: str, salt_str: str) -> str:
    """
    Generate argon2id hash matching Copyparty PWHash (+urlsafe tail format).

    - Input is the exact plaintext Copyparty hashes (with usernames enabled: "username:password")
    - Salt is used as-is (UTF-8 bytes of the string), like Copyparty PWHash
    - Output is in the short "+<urlsafe_base64_tail>" form
    """
    # Copyparty PWHash defaults (see copyparty/pwhash.py)
    time_cost = 3
    memory_cost_mib = 256
    parallelism = 4
    version = 19
    hash_len = 24  # bytes

    bret = hash_secret(
        secret=plain.encode("utf-8"),
        salt=salt_str.encode("utf-8"),
        time_cost=time_cost,
        memory_cost=memory_cost_mib * 1024,  # KiB
        parallelism=parallelism,
        hash_len=hash_len,
        type=ArgonType.ID,
        version=version,
    )
    # Extract tail and make URL-safe like Copyparty does
    tail = bret.split(b"$")[-1].decode("utf-8").replace("/", "_").replace("+", "-")
    return "+" + tail


def main():
    if len(sys.argv) != 4:
        print(__doc__)
        sys.exit(1)

    hostname = sys.argv[1]
    username = sys.argv[2]
    password = sys.argv[3]

    print(f"Reading ah_salt for {hostname}...", file=sys.stderr)
    ah_salt = get_ah_salt(hostname)
    print(f"✓ Found ah_salt: {ah_salt[:20]}...", file=sys.stderr)

    print(f"Generating hash for '{username}:***' (usernames mode)...", file=sys.stderr)
    print(
        "NOTE: Hashing 'username:password' to match Copyparty PWHash", file=sys.stderr
    )
    password_hash = generate_copyparty_hash(f"{username}:{password}", ah_salt)

    print(f"\n✓ Generated hash:", file=sys.stderr)
    print(password_hash)

    print(f"\nAdd to host_vars/{hostname}.yml:", file=sys.stderr)
    print(f"  accounts:", file=sys.stderr)
    print(f"    - user: {username}", file=sys.stderr)
    print(f'      password: "{password_hash}"', file=sys.stderr)


if __name__ == "__main__":
    main()
