"""
Console script for blind_files.

nounlist from http://www.desiquintans.com/downloads/nounlist/nounlist.txt

"""

import csv
import sys
from itertools import permutations
from pathlib import Path

import click

from blind_files.aho_corasick_path_generator import AhoCorasickPathGenerator
from blind_files.delimiter_path_generator import DelimiterPathGenerator
from blind_files.identifier_mapper import IdentifierMapper


@click.command()
@click.option(
    "--key",
    "-k",
    default="key",
    help=(
        "Key for hash algorithm.  Can be any string.  Different keys will "
        "produce different mappings."
    ),
)
@click.option(
    "--input-dir",
    "-i",
    type=click.Path(exists=True, file_okay=False),
    required=True,
)
@click.option(
    "--output-dir",
    "-o",
    type=click.Path(file_okay=False),
    required=True,
)
@click.option(
    "--mapping-dir",
    "-m",
    type=click.Path(file_okay=False),
    required=True,
    help="Directory to store the mapping csv and blind / unblind scripts.",
)
@click.option(
    "--mode",
    "-x",
    required=True,
    type=click.Choice(["identifiers", "delimiter"]),
    help=(
        "Whether to recursively replace a fixed set of identifiers, or to "
        "replace all text before some delimiter in a flat set of files."
    ),
)
@click.option(
    "--delimiter",
    "-d",
    default=None,
)
@click.option(
    "--identifiers",
    "-t",
    type=click.File("r"),
    default="-",
    help="The identifiers to be blinded when they occur in any path.",
)
def main(key, input_dir, output_dir, mapping_dir, mode, delimiter, identifiers):
    """Generate a mapping and bash script to blind files."""
    input_dir = Path(input_dir)
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)
    mapping_dir = Path(mapping_dir)
    mapping_dir.mkdir(parents=True, exist_ok=True)

    if mode == "identifiers":
        identifiers = [identifier.strip() for identifier in identifiers]

        for identifier1, identifier2 in permutations(identifiers, 2):
            if identifier1 in identifier2:
                raise click.UsageException(
                    f"{identifier1} is a substring of {identifier2}"
                )

    elif mode == "delimiter":
        if not delimiter:
            raise click.UsageException("Must specify a delimiter")

    mapping_path = mapping_dir / "mapping.csv"
    if mapping_path.exists():
        with open(mapping_path) as mapping_file:
            mapping_reader = csv.reader(mapping_file)
            next(mapping_reader)
            mapping = {original: mapped for original, mapped in mapping_reader}
    else:
        mapping = {}

    identifier_mapper = IdentifierMapper(key)

    reverse_mapping = {mapped: original for original, mapped in mapping.items()}

    blind_script = ""
    unblind_script = ""

    if mode == "delimiter":
        path_generator = DelimiterPathGenerator(identifier_mapper, delimiter)
    else:
        path_generator = AhoCorasickPathGenerator(identifier_mapper, identifiers)

    for source_path, dest_path in path_generator(input_dir, output_dir):
        blind_script += f'mv "{source_path}" "{dest_path}"\n'
        unblind_script += f'mv "{dest_path}" "{source_path}"\n'

    for identifier in path_generator.identifiers:
        mapped = identifier_mapper(identifier)
        if reverse_mapping.setdefault(mapped, identifier) != identifier:
            raise Exception(
                f"Hash collision from '{identifier}' and "
                f"'{reverse_mapping[mapped]}' to '{mapped}'"
            )
        if mapping.setdefault(identifier, mapped) != mapped:
            raise Exception(
                f"Inconsistent hash from '{identifier}' to '{mapped}' and "
                f"'{mapping[identifier]}'"
            )

    with open(mapping_dir / "blind.sh", "a") as out:
        out.write(path_generator.init_lines)
        out.write(blind_script)
    with open(mapping_dir / "unblind.sh", "a") as out:
        out.write(unblind_script)

    with open(mapping_dir / "mapping.csv", "w") as mapping_file:
        mapping_writer = csv.writer(mapping_file)
        mapping_writer.writerow(["original", "blinded"])

        for identifier, mapped in sorted(mapping.items()):
            mapping_writer.writerow([identifier, mapped])


if __name__ == "__main__":
    sys.exit(main())  # pragma: no cover
