#!/usr/bin/env python3
"""
set_password.py — Encrypt and store a password in any section of credentials.ini

Usage:
    python set_password.py                          # interactive prompts
    python set_password.py -s ipmi_credentials      # pre-select section
    python set_password.py --credentials /path/to/credentials.ini
    python set_password.py --keyfile /path/to/secret.key

The first run generates a secret.key file next to credentials.ini.
Keep that key file secure (chmod 600); without it, passwords cannot be decrypted.

Decrypting at runtime (in other scripts):
    from cryptography.fernet import Fernet
    key = open("secret.key", "rb").read()
    fernet = Fernet(key)
    plaintext = fernet.decrypt(encrypted_value.encode()).decode()
"""
import argparse
import configparser
import getpass
import os
import sys

try:
    from cryptography.fernet import Fernet
except ImportError:
    sys.exit("Error: 'cryptography' package is required.  Run: pip install cryptography")


SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
DEFAULT_CREDENTIALS = os.path.join(SCRIPT_DIR, "credentials.ini")
DEFAULT_KEYFILE = os.path.join(SCRIPT_DIR, "secret.key")


def load_or_create_key(keyfile: str) -> bytes:
    """Load an existing Fernet key or generate and save a new one."""
    if os.path.exists(keyfile):
        with open(keyfile, "rb") as fh:
            return fh.read()

    key = Fernet.generate_key()
    with open(keyfile, "wb") as fh:
        fh.write(key)
    # Restrict permissions on Unix-like systems
    try:
        os.chmod(keyfile, 0o600)
    except OSError:
        pass
    print(f"New encryption key created: {keyfile}")
    print("Keep this file secure — it is required to decrypt passwords.")
    return key


def encrypt_password(plaintext: str, fernet: Fernet) -> str:
    return fernet.encrypt(plaintext.encode()).decode()


def main():
    parser = argparse.ArgumentParser(
        description="Encrypt and store a password in credentials.ini"
    )
    parser.add_argument(
        "--credentials",
        default=DEFAULT_CREDENTIALS,
        help=f"Path to credentials file (default: {DEFAULT_CREDENTIALS})",
    )
    parser.add_argument(
        "--keyfile",
        default=DEFAULT_KEYFILE,
        help=f"Path to encryption key file (default: {DEFAULT_KEYFILE})",
    )
    parser.add_argument(
        "-s",
        "--section",
        help="Section to update (skips interactive section prompt)",
    )
    args = parser.parse_args()

    config = configparser.ConfigParser()

    if not os.path.exists(args.credentials):
        print(f"Credentials file not found. Creating: {args.credentials}")
        os.makedirs(os.path.dirname(os.path.abspath(args.credentials)), exist_ok=True)
        # Seed with the standard sections and placeholder values
        config["credentials"] = {"username": "", "password": "", "domain": ""}
        config["ipmi_credentials"] = {
            "username": "",
            "password": "",
            "IPMIEK": "0000000000000000000000000000000000000000",
        }
        config["ilo_credentials"] = {"username": "", "password": ""}
        with open(args.credentials, "w") as fh:
            config.write(fh)
        try:
            os.chmod(args.credentials, 0o600)
        except OSError:
            pass
        print(f"Created {args.credentials} — fill in usernames and re-run to set passwords.")
    else:
        config.read(args.credentials)

    sections = config.sections()

    if not sections:
        sys.exit(f"Error: no sections found in {args.credentials}")

    # Resolve section
    if args.section:
        section = args.section
        if section not in sections:
            sys.exit(
                f"Error: section [{section}] not found in {args.credentials}\n"
                f"Available sections: {', '.join(sections)}"
            )
    else:
        print("Available sections:")
        for i, s in enumerate(sections, 1):
            print(f"  {i}. {s}")
        choice = input("Select section number: ").strip()
        try:
            section = sections[int(choice) - 1]
        except (ValueError, IndexError):
            sys.exit("Error: invalid selection")

    # Prompt for new password (hidden input, confirmed)
    while True:
        password = getpass.getpass(f"New password for [{section}]: ")
        confirm = getpass.getpass("Confirm password: ")
        if password == confirm:
            break
        print("Passwords do not match. Try again.")

    # Load/create key and encrypt
    key = load_or_create_key(args.keyfile)
    fernet = Fernet(key)
    encrypted = encrypt_password(password, fernet)

    config.set(section, "password", encrypted)

    with open(args.credentials, "w") as fh:
        config.write(fh)

    print(f"Password updated and encrypted in [{section}] of {args.credentials}")


if __name__ == "__main__":
    main()
