"""
This file is a duplicate of
https://github.com/equinor/semeio/blob/master/semeio/jobs/design_kw/design_kw.py

It is copied here instead of pip-installed in order to avoid dragging
along all dependencies of semeio"""

from __future__ import annotations

import re
import shlex
from typing import Any, Final, Iterable

from ._logging import null_logger

_STATUS_FILE_NAME: Final = "DESIGN_KW.OK"

_logger: Final = null_logger(__name__)


def run(
    template_file_name: str,
    result_file_name: str,
    log_level: str,
    parameters_file_name: str = "parameters.txt",
) -> None:
    # Get all key, value pairs
    # If FWL key is having multiple entries in the parameters file
    # KeyError is raised. This will be logged, and no OK
    # file is written
    _logger.setLevel(log_level)

    with open(parameters_file_name) as parameters_file:
        parameters = parameters_file.readlines()

    key_vals = extract_key_value(parameters)

    key_vals.update(rm_genkw_prefix(key_vals))

    with open(template_file_name) as template_file:
        template = template_file.readlines()

    valid = True
    with open(result_file_name, "w") as result_file:
        for line in template:
            if not is_comment(line):
                for key, value in key_vals.items():
                    line = line.replace(f"<{key}>", str(value))

                if not all_matched(line, template_file_name, template):
                    valid = False

            result_file.write(line)

    if valid:
        with open(_STATUS_FILE_NAME, "w") as status_file:
            status_file.write("DESIGN_KW OK\n")


def all_matched(line: str, template_file_name: str, template: list[str]) -> bool:
    valid = True
    for unmatched in unmatched_templates(line):
        if is_perl(template_file_name, template):
            _logger.warning(  # pylint: disable=logging-fstring-interpolation
                f"{unmatched} not found in design matrix, "
                f"but this is probably a Perl file"
            )
        else:
            _logger.error(  # pylint: disable=logging-fstring-interpolation
                f"{unmatched} not found in design matrix"
            )
            valid = False
    return valid


def is_perl(file_name: str, template: list[str]) -> bool:
    return bool(file_name.endswith(".pl") or template[0].find("perl") != -1)


def unmatched_templates(line: str) -> list[str]:
    bracketpattern = re.compile("<.+?>")
    if bracketpattern.search(line):
        return bracketpattern.findall(line)
    return []


def is_comment(line: str) -> bool:
    ecl_comment_pattern = re.compile("^--")
    std_comment_pattern = re.compile("^#")
    return bool(ecl_comment_pattern.search(line) or std_comment_pattern.search(line))


def extract_key_value(parameters: Iterable[str]) -> dict[str, str]:
    """Parses a list of strings, looking for key-value pairs pr. line
    separated by whitespace, into a dictionary.

    Spaces in keys and/or values are supported if quoted. Quotes
    in keys/values are not supported.

    Args:
        parameters (list of str)

    Returns:
        dict, with the keys and values parsed.

    Raises:
        ValueError, with error messages and all unparseable lines.
    """
    res = {}
    errors = []
    for line in parameters:
        line_parts = shlex.split(line)
        if not line_parts:
            continue
        if len(line_parts) == 1:
            errors += [f"No value found in line {line}"]
            continue
        if len(line_parts) > 2:
            errors += [f"Too many values found in line {line}"]
            continue
        key, value = line_parts
        if key in res:
            errors += [f"{key} is defined multiple times"]
            continue
        res[key] = value
    if errors:
        raise ValueError("\n".join(errors))
    return res


def rm_genkw_prefix(
    paramsdict: dict[str, Any],
    ignoreprefixes: str | list[str] | None = "LOG10_",
) -> dict[str, Any]:
    """Strip prefixes from keys in a dictionary.

    Prefix is any string before a colon. No colon means no prefix.

    Only keys unique after prefix-stripping
    are included. For intentional duplicates, as when ERT
    prepares LOG10_ values, these are ignored by default in this
    function.

    Args:
        paramsdict (dict): Dictionary with parameter names as keys.
        ignoreprefixes (str or list of str): If any of these strings
            are found at the start of the prefix, they are removed
            from the dictionary before uniqueness is determined.

    Returns:
        Subset of the incoming dictionary (ignored keys are dropped), and with
        stripped prefixes from keys.
    """
    if ignoreprefixes is None:
        ignoreprefixes = []
    if isinstance(ignoreprefixes, str):
        ignoreprefixes = [ignoreprefixes]

    ignoreprefixes = list(filter(None, ignoreprefixes))

    for ignore_str in ignoreprefixes:
        paramsdict = {
            key: paramsdict[key]
            for key in paramsdict
            if ":" not in key or not key.startswith(ignore_str)
        }

    keyvalues = [
        (key.split(":")[1], value) if ":" in key else (key, value)
        for key, value in paramsdict.items()
    ]

    keys = [keyval[0] for keyval in keyvalues]

    duplicates = {keyvalue[0] for keyvalue in keyvalues if keys.count(keyvalue[0]) > 1}
    if duplicates:
        _logger.warning(f"Key(s) {list(duplicates)} can only be used with prefix.")

    return {
        keyvalue[0]: keyvalue[1]
        for keyvalue in keyvalues
        if keys.count(keyvalue[0]) == 1
    }
