"""Converts SPASE records into schema.org JSON-LD files."""

import os
from pathlib import Path
import json
from soso.main import convert
from soso.strategies.spase.spase import (
    get_temporal,
    get_measurement_method,
    SPASE,
    get_alternate_name,
    get_mentions,
    get_is_part_of,
    get_instrument,
    get_problematic_records,
)

# pylint: disable=too-many-locals
# pylint: disable=raise-missing-from
# pylint: disable=too-many-statements


def get_paths(entry: str, paths: list) -> list:
    """Takes the absolute path of a SPASE record directory to be walked
    to extract all SPASE records present, or a text file containing
    ResourceID(s). Returns these paths using the list parameter paths,
    which holds the absolute paths generated by the function.

    :param entry: A string of the absolute path of the SPASE record directory
                    to be searched/walked to find all SPASE records within, or
                    a string containing the file path to a text file containing
                    ResourceID(s).
    :param paths: A list to hold absolute paths of all SPASE records found
                    within the given directory/text file.
    :return: A list containing the absolute paths of all SPASE records found
                within the given directory/text file.
    """

    # if given folder/directory
    if not os.path.isfile(entry):
        if os.path.exists(entry):
            for root, _, files in os.walk(entry):
                if files:
                    for file in files:
                        paths.append(root + "/" + file)
    # if given a file containing SPASE record names
    elif os.path.isfile(entry):
        lines = []
        home_dir = str(Path.home()).replace("\\", "/")
        with open(entry, "r", encoding="utf-8") as f:
            lines = f.read().splitlines()
        for resource_id in lines:
            formatted_record = home_dir + "/" + resource_id.replace("spase://", "")
            if not formatted_record.endswith(".xml"):
                formatted_record += ".xml"
            paths.append(formatted_record)
    else:
        print(entry + " does not exist")
    return paths


def find_requirements(folder: str) -> None:
    """
    Searches through the SPASE records in the given directory to find out which external
    repositories are needed in order for the main script to run properly.

    :param folder: The path to the directory containing SPASE records that the user wishes to
        check the external repository requirements for.
    """
    # call creator, contributor, instrument, and observatory functions to find repos
    #   needed to be cloned
    # for NASA SPASE = SMWG, NASA, ESA

    # get current working directory
    cwd = str(Path.cwd())
    # create log file that holds name of repos needed
    with open(f"{cwd}/requiredRepos.txt", "w", encoding="utf-8") as f:
        f.write(
            "Please git clone the following SPASE repositories in your home directory "
            "for the script to run as intended:"
        )

    # list that holds SPASE records already checked
    searched = []

    # obtains all filepaths to all SPASE records found in given directory
    spase_paths = []
    spase_paths = get_paths(folder, spase_paths)
    # print("You entered " + folder)
    if len(spase_paths) == 0:
        print(
            "No records found. Make sure the directory path is correct and try again."
        )
    else:
        print("Looking through files to see what repos are needed locally. One moment.")
        # iterate through all SPASE records
        for record in spase_paths:
            if record not in searched:
                # print(record)

                # scrape metadata for each record to find out what repos are needed locally
                test_spase = SPASE(record)
                test_spase.get_creator()
                test_spase.get_contributor()
                try:
                    get_instrument(test_spase.metadata, record)
                    # get_observatory(test_spase.metadata, record)
                    get_is_part_of(test_spase.metadata, record)
                    get_mentions(test_spase.metadata, record)
                    test_spase.get_was_revision_of()
                    test_spase.get_is_based_on()
                # to ensure that main script will still run in
                #   the event of a broken link
                except AttributeError:
                    pass

                # print()
            # add record to searched
            searched.append(record)
        # get all repos needed from file and present to user
        with open(f"{cwd}/requiredRepos.txt", "r", encoding="utf-8") as f:
            text = f.read()
        print(text)


def make_json_path(record: str) -> tuple[str, str]:
    """Takes path to SPASE record and forms a path to hold the
    schema.org JSON outputted by main script"""
    home = str(Path.home()).replace("\\", "/")
    substring = record.split(f"{home}/")
    try:
        substring2 = substring[1].split("/", 1)
        repo_name = substring2[0]
        substring3 = substring2[1].split(".xml")
        path_to_file = substring3[0]
        path_to_file, _, file_name = path_to_file.rpartition("/")
        path_to_file = f"{repo_name}/{path_to_file}"
    except IndexError:
        raise IndexError(
            "File path is incorrect. Ensure that the absolute path is given."
        )

    try:
        os.makedirs(f"./SPASE_JSONs/{path_to_file}")
    except FileExistsError:
        pass

    return path_to_file, file_name


def main(folder: str, additional_license_info: bool = None) -> None:
    """
    Scrapes all desired metadata from the given SPASE records and exports them as schema.org JSONs
    in the current working directory, following a similar directory structure as they appear in the
    SPASE directory.

    :param folder: The path to the directory/text file containing SPASE records that the user
        wishes to create schema.org JSONs for
    :param additional_license_info: An optional argument used to pass an additional
        metadata license not currently included within the common_licenses list in the
        get_subject_of function in spase.py. The format should follow:
        [<full name> <identifier> <url>]. Refer to the spase-HowToConvert Jupyter notebook
        for more information.
    """
    # run pre-script which informs user which repos are needed for the main script
    find_requirements(folder)
    input("Once these are cloned, type anything to begin the main script. ")

    # list that holds SPASE records already checked
    searched = []

    # obtains all filepaths to all SPASE records found in given directory
    spase_paths = []
    spase_paths = get_paths(folder, spase_paths)
    # print("You entered " + folder)
    num = 0
    problematic_records = ""

    if len(spase_paths) == 0:
        print(
            "No records found. Make sure the directory path is correct and try again."
        )
    else:
        # iterate through all SPASE records
        for r, record in enumerate(spase_paths):
            if record not in searched:
                # print name and number of record being scraped
                status_message = f"\r\033[KExtracting metadata from record {r+1}"
                status_message += f" of {len(spase_paths)}"
                print(status_message, end="")
                # print(record)
                print()
                num = r

                # create path to schema.org output json
                path_to_file, file_name = make_json_path(record)

                # scrape metadata for each record
                test_spase = SPASE(record)
                # if more licenseInfo is given, overwrite the subjectOf function to reflect that
                if additional_license_info:
                    subject_of = test_spase.get_subject_of(*additional_license_info)
                temporal = get_temporal(test_spase.metadata, test_spase.namespaces)
                alternate_name = get_alternate_name(test_spase.metadata)
                in_language = "en"
                mentions = get_mentions(test_spase.metadata, record)
                is_part_of = get_is_part_of(test_spase.metadata, record)
                measurement_method = get_measurement_method(
                    test_spase.metadata, test_spase.namespaces
                )
                problematic_records = get_problematic_records()
                # add record to searched
                searched.append(record)

                # additional schema.org properties not supported by SOSO
                kwargs = {
                    "temporal": temporal,
                    "alternateName": alternate_name,
                    "inLanguage": in_language,
                    "mentions": mentions,
                    "isPartOf": is_part_of,
                    "measurementMethod": measurement_method,
                }
                if additional_license_info:
                    kwargs["subjectOf"] = subject_of

                # create schema.org JSON
                creation = convert(file=record, strategy="SPASE")
                updated_dict = json.loads(creation)
                # add sosa ontology to json "@context"
                updated_dict["@context"][
                    "sosa"
                ] = "https://w3c.github.io/sdw-sosa-ssn/ssn/#SOSA"
                # update json to include nonSOSO-supported fields
                updated_dict.update(kwargs)

                # create json file using the result from convert method
                with open(
                    f"./SPASE_JSONs/{path_to_file}/{file_name}.json",
                    "w",
                    encoding="utf-8",
                ) as f:
                    json.dump(updated_dict, f, indent=3, sort_keys=True)

                # if wish to see python printout instead
                # from pprint import pprint
                # finalDict = updated_dict | kwargs
                # pprint(finalDict)
        print(f"{num + 1} records successfully converted to schema.org JSONs")
        # print(problematic_records)
        if problematic_records:
            if "," in problematic_records:
                num_of_problems = problematic_records.count(",") + 1
            else:
                num_of_problems = 1
        else:
            num_of_problems = 0
        # Let user know which SPASE records caused issues for further analysis
        if num_of_problems > 0:
            print(
                f"The script had issues accessing {num_of_problems} of these files,"
                + f" which are: {problematic_records}"
            )
        else:
            print(f"The script had issues accessing {num_of_problems} of these files")
        # delete temporary file
        Path(f"{str(Path.cwd())}/requiredRepos.txt").unlink()


# allow calls from the command line
if __name__ == "__main__":
    from sys import argv

    if len(argv) == 1:
        HOME_DIR = str(Path.home()).replace("\\", "/")
        print(help(main))
        print()
        print(
            "A good test record to try if scraping the NASA SPASE repo is in this folder: "
            f"{HOME_DIR}/.../NASA/NumericalData/MMS/4/HotPlasmaCompositionAnalyzer/Burst/Level2/Ion"
        )
        print(
            "Rerun the script again, passing the SPASE repo (or a specific folder within it)"
            " that you want to create schema.org JSONs for as an argument"
        )
    else:
        if len(argv) == 2 and argv[1] == "--help":
            print(help(main))
        else:
            if "\\" in str(argv[1]):
                argv[1] = argv[1].replace("\\", "/")
            if len(argv) > 2:
                main(argv[1], [argv[2], argv[3], argv[4]])
            else:
                main(argv[1])
