"""
Module for CLI command
"""
import os
import stat
from pathlib import Path

import click
import toml

from archimedes_config.utils.path_utils import clean_filename, find_repo_root
from archimedes_config.utils.util import (
    RegisteredConfigDecrypted,
    check_encrypted,
    get_registered_configs,
    get_secret,
    register_config,
    register_secret,
    unregister_config,
)
from archimedes_config.workflows.add_secrets import AddSecret
from archimedes_config.workflows.lock_secrets import LockConfig
from archimedes_config.workflows.secret_creation import ConfigCreator
from archimedes_config.workflows.unlock_secrets import UnlockConfig

COMMAND_NAME = "arckeyl"
PRE_COMMIT_CHECK_FILE_NAME = f".{COMMAND_NAME}_config"
SECRET_KEYS_FILE_NAME = f".{COMMAND_NAME}_secrets"


@click.group(context_settings={"help_option_names": ["-h", "--help"]})
def cli():
    """
    Welcome to The Archimedes Config Manager.

    \b
    Commands:
        {COMMAND_NAME} new        Create a new config.
        {COMMAND_NAME} add        Add new keys to an existing config.
        {COMMAND_NAME} lock       Locks an existing config.
        {COMMAND_NAME} unlock     Unlocks an existing config.
        {COMMAND_NAME} check      Ensures all registered configs are encrypted.
        {COMMAND_NAME} register      Register config to pre-commit hook.
        {COMMAND_NAME} unregister   Unregister config from pre-commit hook
    """.format(
        COMMAND_NAME=COMMAND_NAME
    )


@cli.command("init")
def init():
    """Initializes pre-commit"""

    root_path = find_repo_root()

    # CREATE CONFIG REGISTRY
    config_path = str(root_path / PRE_COMMIT_CHECK_FILE_NAME)
    if not Path(config_path).exists():
        print("Arckeyl config does not exist. Creating a new config...")
        with open(config_path, "w", encoding="utf8") as file:
            toml.dump({"registered_configs": []}, file)

    # CREATE SECRET REGISTRY
    secrets_path = root_path / SECRET_KEYS_FILE_NAME
    if not secrets_path.exists():
        print("Arckeyl secrets does not exist. Creating a new secrets...")
        with open(secrets_path, "w", encoding="utf8") as file:
            toml.dump({}, file)

    # Add secrets to gitignore
    ignore_path = root_path / ".gitignore"
    if ignore_path.exists():
        with open(ignore_path, "r", encoding="utf8") as file:
            lines = file.readlines()
        if SECRET_KEYS_FILE_NAME not in lines:
            print("Adding secrets to existing gitignore")
            with open(ignore_path, "a", encoding="utf8") as file:
                file.write(f"\n{SECRET_KEYS_FILE_NAME}")
    else:
        print("Created .gitignore and added secrets to it")
        with open(ignore_path, "a", encoding="utf8") as file:
            file.write(f"\n{SECRET_KEYS_FILE_NAME}")

    # CREATE PRE-COMMIT HOOK
    pre_commit_path = root_path / ".git" / "hooks" / "pre-commit"
    command = f"poetry run {COMMAND_NAME} check"
    try:
        with open(pre_commit_path, "r", encoding="utf8") as file:
            lines = file.readlines()
        if command not in lines:
            lines.append(f"\n{command}")
            print("Added encryption check to existing pre-commit webhook")
            with open(pre_commit_path, "w", encoding="utf8") as file:
                file.writelines(lines)
    except FileNotFoundError:
        print("Creating pre commit webhook")
        with open(pre_commit_path, "w", encoding="utf8") as file:
            sample = f"#!/bin/bash\n\n{command}"
            file.write(sample)
    try:
        os.chmod(
            pre_commit_path,
            os.stat(pre_commit_path).st_mode
            | stat.S_IXUSR  # user execute
            | stat.S_IXGRP  # group execute
            | stat.S_IXOTH,  # others execute
        )
    except:  # pylint:disable=bare-except
        print(
            f"Please make sure that the pre commit file located in `{pre_commit_path}` is executable."
        )


@cli.command("add")
@click.argument("file_name")
@clean_filename
def add(file_name):
    """Add a secret to the config"""
    AddSecret().interactive(file_name)


@cli.command("new")
@click.argument("file_name")
@clean_filename
def new(file_name):
    """Create a new config"""
    created_config = ConfigCreator()
    created_config.interactive(file_name)
    repo_root = find_repo_root()
    register_config(file_name, str(repo_root / PRE_COMMIT_CHECK_FILE_NAME))
    if created_config.new_key_generated:
        register_secret(
            file_name,
            str(repo_root / SECRET_KEYS_FILE_NAME),
            created_config.conf.default_key,
        )


@cli.command("lock")
@click.argument("file_name", required=False)
@clean_filename
def lock(file_name):
    """Locks the config"""
    repo_root = find_repo_root()
    if file_name:
        LockConfig().interactive(
            file_name,
            get_secret(
                file_name, str(repo_root / SECRET_KEYS_FILE_NAME), default_return=None
            ),
        )
        return
    config_path = str(repo_root / PRE_COMMIT_CHECK_FILE_NAME)
    configs = get_registered_configs(config_path)
    for config in configs:
        config = str(repo_root / config)
        secret = get_secret(
            config, str(repo_root / SECRET_KEYS_FILE_NAME), default_return=None
        )
        LockConfig().interactive(config, secret)
    print("Locked all configs.")


@cli.command("unlock")
@click.argument("file_name", required=False)
@clean_filename
def unlock(file_name):
    """Unlocks the config"""
    repo_root = find_repo_root()
    if file_name:
        UnlockConfig().interactive(
            file_name, get_secret(file_name, str(repo_root / SECRET_KEYS_FILE_NAME))
        )
        return

    config_path = str(repo_root / PRE_COMMIT_CHECK_FILE_NAME)
    configs = get_registered_configs(config_path)
    for config in configs:
        config = str(repo_root / config)
        secret = get_secret(
            config, str(repo_root / SECRET_KEYS_FILE_NAME), default_return=None
        )
        UnlockConfig().interactive(config, secret)
    print("Unlocked all configs.")


@cli.command("register")
@click.argument("file_name")
@clean_filename
def register(file_name):
    """Adds config file to pre-commit registry"""
    config_path = str(find_repo_root() / PRE_COMMIT_CHECK_FILE_NAME)
    register_config(file_name, config_path)


@cli.command("check")
@clean_filename
def check():
    """Checks encryption status of all registered configs"""
    config_path = str(find_repo_root() / PRE_COMMIT_CHECK_FILE_NAME)
    try:
        check_encrypted(config_path)
    except RegisteredConfigDecrypted as err:
        raise click.ClickException(str(err))


@cli.command("unregister")
@click.argument("file_name")
@clean_filename
def unregister(file_name):
    """Unregister a config from pre-commit registry"""
    config_path = str(find_repo_root() / PRE_COMMIT_CHECK_FILE_NAME)
    unregister_config(file_name, config_path)
