"""Utilities for dealing with .strings files"""

import os
from typing import Dict, List, Optional, Set

from dotstrings.parser import load, loads
from dotstrings.dot_strings_entry import DotStringsEntry
from dotstrings.localized_bundle import LocalizedBundle
from dotstrings.localized_string import LocalizedString


def language_folder_path(strings_folder: str, language: str) -> str:
    """Generate the folder path for a language.

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code for the file

    :returns: The path to the strings file
    """
    return os.path.join(strings_folder, f"{language}.lproj")


def strings_file_path(strings_folder: str, language: str, table_name: str) -> str:
    """Generate the .strings file path from the details supplied.

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code for the file
    :param table_name: The name of the table (file name)

    :returns: The path to the strings file
    """
    return os.path.join(language_folder_path(strings_folder, language), f"{table_name}.strings")


def languages_in_folder(strings_folder: str) -> Set[str]:
    """Find all the languages in a folder

    This looks for *.lproj folders.

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)

    :returns: A set containing all the languages.
    """
    languages = os.listdir(strings_folder)
    languages = [language for language in languages if language.endswith(".lproj")]
    return {language.replace(".lproj", "") for language in languages}


def load_table(strings_folder: str, language: str, table_name: str) -> List[LocalizedString]:
    """Load the specified .strings table

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code to load
    :param table_name: The name of the table to load

    :returns: A list of strings in the table
    """

    table_path = strings_file_path(strings_folder, language, table_name)
    strings = load(table_path)
    return LocalizedString.from_dotstring_entries(
        entries=strings, language=language, table=table_name
    )


def load_language_tables(strings_folder: str, language: str) -> Dict[str, List[LocalizedString]]:
    """Load the .strings tables for a given language

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code to load

    :returns: A dictionary of results. The key is the table, the value is the
              list of strings in the table
    """

    language_folder = language_folder_path(strings_folder, language)

    results = {}

    for table in os.listdir(language_folder):
        if not table.endswith(".strings"):
            continue
        table_name = table.replace(".strings", "")
        results[table_name] = load_table(strings_folder, language, table_name)

    return results


def load_all_strings(strings_folder: str) -> LocalizedBundle:
    """Load the .strings tables for a given language

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code to load

    :returns: A LocalizedBundle containing the results
    """

    languages = languages_in_folder(strings_folder)

    results = {}

    for language in sorted(languages):
        results[language] = load_language_tables(strings_folder, language)

    return LocalizedBundle(results)


def normalize(
    strings_path: str,
    output_path: Optional[str] = None,
    remove_duplicates: bool = True,
    sort_comments: bool = True,
) -> None:
    """Load in the specified .strings table, normalize it, and write it back out.

    Normalizing consists of sorting by key, then comments, and (usually)
    deduplicating before writing back out.

    :param strings_path: The location of the strings file
    :param output_path: The location to write the sorted file to. If None, it
                        will overwrite the input file.
    :param remove_duplicates: By default, any duplicates will be removed and
                              their comments combined. Set to False to raise
                              an exception instead
    :param sort_comments: By default comments will be sorted in alphabetical
                          order. However, if you use linebreaks in your comments
                          you will wish to turn this off. It also removes
                          duplicates.
    """

    entries = load(strings_path)
    entries.sort(key=lambda x: [x.key] + x.comments)

    deduped_entries = [entries[0]]

    for entry in entries[1:]:

        # If the keys are different, just add to the list and move on
        if deduped_entries[-1].key != entry.key:
            deduped_entries.append(entry)
            continue

        # If we have duplicate keys but the values don't match, that's an
        # exception, whether or not we are removing duplicates
        if deduped_entries[-1].value != entry.value or not remove_duplicates:
            raise Exception(f"Found duplicate strings with key: {entry.key}")

        deduped_entries[-1].comments.extend(entry.comments)

    if sort_comments:
        for entry in deduped_entries:
            entry.comments = list(set(entry.comments))
            entry.comments.sort()

    if output_path is None:
        output_path = strings_path

    with open(output_path, "w") as strings_file:
        for entry in deduped_entries:
            strings_file.write(entry.strings_format())
            strings_file.write("\n\n")

"""Utilities for dealing with .strings files"""

import os
from typing import Dict, List, Optional, Set

from dotstrings.parser import load, loads
from dotstrings.dot_strings_entry import DotStringsEntry
from dotstrings.localized_bundle import LocalizedBundle
from dotstrings.localized_string import LocalizedString


def language_folder_path(strings_folder: str, language: str) -> str:
    """Generate the folder path for a language.

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code for the file

    :returns: The path to the strings file
    """
    return os.path.join(strings_folder, f"{language}.lproj")


def strings_file_path(strings_folder: str, language: str, table_name: str) -> str:
    """Generate the .strings file path from the details supplied.

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code for the file
    :param table_name: The name of the table (file name)

    :returns: The path to the strings file
    """
    return os.path.join(language_folder_path(strings_folder, language), f"{table_name}.strings")


def languages_in_folder(strings_folder: str) -> Set[str]:
    """Find all the languages in a folder

    This looks for *.lproj folders.

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)

    :returns: A set containing all the languages.
    """
    languages = os.listdir(strings_folder)
    languages = [language for language in languages if language.endswith(".lproj")]
    return {language.replace(".lproj", "") for language in languages}


def load_table(strings_folder: str, language: str, table_name: str) -> List[LocalizedString]:
    """Load the specified .strings table

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code to load
    :param table_name: The name of the table to load

    :returns: A list of strings in the table
    """

    table_path = strings_file_path(strings_folder, language, table_name)
    strings = load(table_path)
    return LocalizedString.from_dotstring_entries(
        entries=strings, language=language, table=table_name
    )


def load_language_tables(strings_folder: str, language: str) -> Dict[str, List[LocalizedString]]:
    """Load the .strings tables for a given language

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code to load

    :returns: A dictionary of results. The key is the table, the value is the
              list of strings in the table
    """

    language_folder = language_folder_path(strings_folder, language)

    results = {}

    for table in os.listdir(language_folder):
        if not table.endswith(".strings"):
            continue
        table_name = table.replace(".strings", "")
        results[table_name] = load_table(strings_folder, language, table_name)

    return results


def load_all_strings(strings_folder: str) -> LocalizedBundle:
    """Load the .strings tables for a given language

    :param strings_folder: The location of the strings folder (which contains
                           all the *.lproj folders)
    :param language: The language code to load

    :returns: A LocalizedBundle containing the results
    """

    languages = languages_in_folder(strings_folder)

    results = {}

    for language in sorted(languages):
        results[language] = load_language_tables(strings_folder, language)

    return LocalizedBundle(results)


def normalize(
    strings_path: str,
    output_path: Optional[str] = None,
    remove_duplicates: bool = True,
    sort_comments: bool = True,
) -> None:
    """Load in the specified .strings table, normalize it, and write it back out.

    Normalizing consists of sorting by key, then comments, and (usually)
    deduplicating before writing back out.

    :param strings_path: The location of the strings file
    :param output_path: The location to write the sorted file to. If None, it
                        will overwrite the input file.
    :param remove_duplicates: By default, any duplicates will be removed and
                              their comments combined. Set to False to raise
                              an exception instead
    :param sort_comments: By default comments will be sorted in alphabetical
                          order. However, if you use linebreaks in your comments
                          you will wish to turn this off. It also removes
                          duplicates.
    """

    entries = load(strings_path)
    entries.sort(key=lambda x: [x.key] + x.comments)

    deduped_entries = [entries[0]]

    for entry in entries[1:]:

        # If the keys are different, just add to the list and move on
        if deduped_entries[-1].key != entry.key:
            deduped_entries.append(entry)
            continue

        # If we have duplicate keys but the values don't match, that's an
        # exception, whether or not we are removing duplicates
        if deduped_entries[-1].value != entry.value or not remove_duplicates:
            raise Exception(f"Found duplicate strings with key: {entry.key}")

        deduped_entries[-1].comments.extend(entry.comments)

    if sort_comments:
        for entry in deduped_entries:
            entry.comments = list(set(entry.comments))
            entry.comments.sort()

    if output_path is None:
        output_path = strings_path

    with open(output_path, "w") as strings_file:
        for entry in deduped_entries:
            strings_file.write(entry.strings_format())
            strings_file.write("\n\n")

Utilities for dealing with .strings files Generate the folder path for a language.      :param strings_folder: The location of the strings folder (which contains                            all the *.lproj folders)     :param language: The language code for the file      :returns: The path to the strings file Generate the .strings file path from the details supplied.      :param strings_folder: The location of the strings folder (which contains                            all the *.lproj folders)     :param language: The language code for the file     :param table_name: The name of the table (file name)      :returns: The path to the strings file Find all the languages in a folder      This looks for *.lproj folders.      :param strings_folder: The location of the strings folder (which contains                            all the *.lproj folders)      :returns: A set containing all the languages. Load the specified .strings table      :param strings_folder: The location of the strings folder (which contains                            all the *.lproj folders)     :param language: The language code to load     :param table_name: The name of the table to load      :returns: A list of strings in the table Load the .strings tables for a given language      :param strings_folder: The location of the strings folder (which contains                            all the *.lproj folders)     :param language: The language code to load      :returns: A dictionary of results. The key is the table, the value is the               list of strings in the table Load the .strings tables for a given language      :param strings_folder: The location of the strings folder (which contains                            all the *.lproj folders)     :param language: The language code to load      :returns: A LocalizedBundle containing the results Load in the specified .strings table, normalize it, and write it back out.      Normalizing consists of sorting by key, then comments, and (usually)     deduplicating before writing back out.      :param strings_path: The location of the strings file     :param output_path: The location to write the sorted file to. If None, it                         will overwrite the input file.     :param remove_duplicates: By default, any duplicates will be removed and                               their comments combined. Set to False to raise                               an exception instead     :param sort_comments: By default comments will be sorted in alphabetical                           order. However, if you use linebreaks in your comments                           you will wish to turn this off. It also removes                           duplicates. # If the keys are different, just add to the list and move on # If we have duplicate keys but the values don't match, that's an # exception, whether or not we are removing duplicates