import io
import os
import logging
import requests
import yaml
from functools import lru_cache
from typing import Dict, List

from django.conf import settings
from webdav3.client import Client

from huscy.xml.models import XmlModel


logger = logging.getLogger(__name__)


def load_configuration():
    dir_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    template_name = os.path.join(dir_path, "webdav_credentials.yaml_template")
    filename = os.environ.get("HUSCY_XML_WEBDAV_FILE", template_name)
    print(f"Loading WebDAV configuration from {filename}")
    print("This can be altered by setting environment variable HUSCY_XML_WEBDAV_FILE")
    logger.info(f"Loading WebDAV configuration from {filename}")
    logger.info("This can be altered by setting environment variable HUSCY_XML_WEBDAV_FILE")
    with open(filename, "r") as file:
        config = yaml.safe_load(file)

    # webdav conf
    return {
        "webdav_hostname": config["hostname"],
        "webdav_login": config["username"],
        "webdav_password": config["password"],
        "disable_check": True,
        'disabled': config.get('no_webdav', False),
    }


def _get_setting(key, default):
    huscy_settings = getattr(settings, 'HUSCY', {})
    xml_settings = huscy_settings.get('xml', {})
    return xml_settings.get(key, default)


if 'xml' in getattr(settings, 'HUSCY', {}):
    webdav_conf = {
        'webdav_hostname': _get_setting('webdav_hostname', ''),
        'webdav_login': _get_setting('webdav_username', ''),
        'webdav_password': _get_setting('webdav_password', ''),
        'disabled': _get_setting('webdav_disabled', True)
    }
else:
    webdav_conf = load_configuration()


if webdav_conf['disabled']:
    logger.info('Running without WebDAV connection created.')
else:
    # Create Connection
    client = Client(webdav_conf)

    # HTTP Request for bigger files
    session = requests.Session()
    session.auth = (webdav_conf["webdav_login"], webdav_conf["webdav_password"])
    # basic_auth = HTTPBasicAuth(webdav_conf["webdav_login"], webdav_conf["webdav_password"])

    logger.debug('Files on the Server:')
    for file in client.list():
        logger.debug(file)


@lru_cache(maxsize=20000)
def get_participations(xml_remote_path: str) -> list:
    if webdav_conf['disabled']:
        import tests.conftest

        xml_data = tests.conftest.test_xml_data
    else:
        buffer = io.BytesIO()
        full_url = f"{webdav_conf['webdav_hostname'].rstrip('/')}/{xml_remote_path.lstrip('/')}"
        response = session.get(full_url)  # , auth=basic_auth)
        response.raise_for_status()
        buffer.write(response.content)

        xml_data = (
            buffer.getvalue()
            .decode("iso-8859-1")
            .replace(' encoding="ISO-8859-1"?>', "?>")
        )

    xml_model = XmlModel.create_from_string(xml_data)

    return xml_model  # .exp_name


@lru_cache(maxsize=20000)
def is_participated(proband_xml: str, experiment_name):  # , session):

    experiments = get_participations(proband_xml).participated_experiments
    for current in experiments:
        new = current.replace(" ", "")
        if experiment_name == new:
            return True
    return False


@lru_cache(maxsize=20000)
def is_scheduled(proband_xml: str, experiment_name):  # , session):

    experiments = get_participations(proband_xml).scheduled_experiments
    for current in experiments:
        new = current.replace(" ", "")
        if experiment_name == new:
            return True
    return False


@lru_cache(maxsize=20000)
def is_invited(proband_xml: str, experiment_name):  # , session):

    experiments = get_participations(proband_xml).invited_experiments
    for current in experiments:
        new = current.replace(" ", "")
        # A regular expression must be entered here, as the experiment name is in the text.
        if experiment_name in new:
            return True
    return False


def get_participations_xml(experiment_name: str) -> List[int]:
    huscy_ids = set()

    base_dir = "/datenbank/Proband"
    if webdav_conf['disabled']:
        liste = ["default_data.xml"]
    else:
        liste = list(client.list(base_dir))
    counter = 0
    all_counter = len(liste)

    for file in liste:
        if file.endswith(".xml"):
            full_path = os.path.join(base_dir, file)
            counter = counter + 1
            if counter % 100 == 0:
                logger.debug(f'Processed: {counter} / {all_counter}', end='\r')
            try:
                if is_participated(full_path, experiment_name):
                    huscy_ids.add(file)
            except Exception as e:
                logger.warning(f'Error processing file {file}: {e}')
    return huscy_ids


def get_participation_status_xml(
    experiment_name: str, pseudonyms: List[str]
) -> Dict[str, int]:
    """
    Provides the participation status code for each subject from the XML DB.

    :param str experiment_name: Name of the experiment to look for in the XML DB.
        Note that spaces are removed from the name
    :param List[str] proband_list: List of subjects given by their Proband ID in the XML DB
    :param str message_body: The body of the message
    :return: the status code for each subjects in the list.
        If XML DB does not contain info for a specific subject, None is returned as its status code.
    :rtype: List[int]
    """

    base_dir = "/datenbank/Proband"
    counter = 0
    all_counter = len(pseudonyms)

    participations = {}

    for pseudonym in pseudonyms:
        status = None
        full_path = os.path.join(base_dir, f"Proband{pseudonym}.xml")
        counter = counter + 1
        if counter % 100 == 0:
            logger.debug(f'Processed: {counter} / {all_counter}', end='\r')
        try:
            if is_participated(full_path, experiment_name):
                status = 2
            elif is_scheduled(full_path, experiment_name):
                status = 1
            # elif is_declined(full_path, experiment_name):
            #     status = 2
            # elif is_no_show(full_path, experiment_name):
            #     status = 3
            # elif is_accepted(full_path, experiment_name):
            #     status = 4
            elif is_invited(full_path, experiment_name):
                status = 0
        except Exception as e:
            logger.warning(f'Error processing file {pseudonym}: {e}')
        participations[pseudonym] = status
    return participations
