from logging import Logger
from typing import Dict, List, Optional, Union

from hdx.location.adminlevel import AdminLevel
from hdx.utilities.dateparse import parse_date
from hdx.utilities.typehint import ListTuple

DEFAULT_SOURCE_DATE_FORMAT = "%b %d, %Y"
DEFAULT_DATE_RANGE_SEPARATOR = "-"


def standardise_datasetinfo_source_date(datasetinfo):
    source_date = datasetinfo.get("source_date")
    if not source_date:
        datasetinfo["source_date"] = None
        return None

    output_source_date = dict()

    def set_source_date(date, hxltag="default_date", startend="end"):
        if isinstance(date, str):
            date = parse_date(date)
        if hxltag not in output_source_date:
            output_source_date[hxltag] = dict()
        output_source_date[hxltag][startend] = date

    if isinstance(source_date, dict):
        for key, value in source_date.items():
            if key in ("start", "end"):
                set_source_date(value, startend=key)
            else:
                if isinstance(value, dict):
                    for startend, date in value.items():
                        set_source_date(date, hxltag=key, startend=startend)
                else:
                    set_source_date(value, hxltag=key)
    else:
        set_source_date(source_date)
    default_date = output_source_date.get("default_date")
    if default_date:
        default_end_date = default_date.get("end")
        if default_end_date:
            datasetinfo["source_date"] = output_source_date
            return default_end_date
    datasetinfo["source_date"] = None
    return None


def get_hxltag_source_date(datasetinfo, hxltag, fallback=False):
    source_date = datasetinfo["source_date"]
    date = source_date.get(hxltag)
    if not date:
        if not fallback:
            return None
        date = source_date["default_date"]
    source_date_format = datasetinfo.get(
        "source_date_format", DEFAULT_SOURCE_DATE_FORMAT
    )
    if isinstance(source_date_format, str):
        start_source_date_format = None
        end_source_date_format = source_date_format
        date_range_separator = None
    else:
        start_source_date_format = source_date_format.get("start")
        end_source_date_format = source_date_format.get("end")
        if not end_source_date_format:
            end_source_date_format = source_date_format["date"]
        date_range_separator = source_date_format.get(
            "separator", DEFAULT_DATE_RANGE_SEPARATOR
        )
    enddate = date["end"].strftime(end_source_date_format)
    startdate = date.get("start")
    if start_source_date_format and startdate:
        startdate = startdate.strftime(start_source_date_format)
        return f"{startdate}{date_range_separator}{enddate}"
    return enddate


def add_source_overwrite(
    hxltags: List[str],
    sources: List[ListTuple],
    source: ListTuple[str],
    logger: Logger,
):
    """Add source to sources preventing duplication

    Args:
        hxltags (List[str]): List of HXL hashtags, one for each source name
        sources (List[ListTuple]): List of sources
        source (ListTuple[str]): Source information
        logger (Logger): Logegr to log warnings to

    Returns:
        None
    """
    hxltag = source[0]
    if hxltag in hxltags:
        logger.warning(f"Overwriting source information for {hxltag}!")
        index = hxltags.index(hxltag)
        sources[index] = source
    else:
        hxltags.append(hxltag)
        sources.append(source)


def add_sources_overwrite(
    hxltags: List[str],
    sources: List[ListTuple],
    sources_to_add: List[ListTuple],
    logger: Logger,
):
    """Add source to sources preventing duplication

    Args:
        hxltags (List[str]): List of HXL hashtags, one for each source name
        sources (List[ListTuple]): List of sources
        sources_to_add (List[ListTuple]): List of sources to add
        logger (Logger): Logegr to log warnings to

    Returns:
        None
    """
    for source in sources_to_add:
        add_source_overwrite(hxltags, sources, source, logger)


def create_source_configuration(
    suffix_attribute: Optional[str] = None,
    admin_sources: bool = False,
    adminlevel: Union[AdminLevel, ListTuple[AdminLevel], None] = None,
    admin_mapping_dict: Optional[Dict] = None,
) -> Optional[Dict]:
    """Create source configuration. If none of the arguments are suppled, source
    configuration is None. suffix_attribute is an attribute to add to the end of
    source HXL hashtags. admin_sources defines whether the admin unit is added as an
    attribute (eg. a country iso3 code like +AFG). admin_level defines one or more
    AdminLevel objects that will be used to map admin pcodes to country iso3 codes. If
    admin_level is defined, admin_sources is assumed to be True. Alternatively,
    admin_mapping_dict can be supplied to define mapping from amin names to attribute
    suffixes.

    Args:
        suffix_attribute (Optional[str]): Suffix to add. Defaults to None.
        admin_sources (bool): Whether source information is per admin unit. Defaults to False.
        adminlevel (Union[AdminLevel, ListTuple[AdminLevel], None]): Admin level(s) mapping. Defaults to None.
        admin_mapping_dict (Optional[Dict]): Admin unit mapping to use. Defaults to None.

    Returns:
         Optional[Dict]: Source configuration dictionary
    """
    source_configuration = dict()
    if suffix_attribute:
        source_configuration["suffix_attribute"] = suffix_attribute
        return source_configuration
    admin_mapping = None
    if adminlevel:
        if isinstance(adminlevel, AdminLevel):
            admin_mapping = adminlevel.pcode_to_iso3
        else:
            admin_mapping = dict()
            for admlevel in adminlevel:
                admin_mapping.update(admlevel.pcode_to_iso3)
    elif admin_mapping_dict:
        admin_mapping = admin_mapping_dict
    if not admin_sources and not admin_mapping:
        return None
    source_configuration["admin_sources"] = True
    if admin_mapping:
        source_configuration["admin_mapping"] = admin_mapping
    return source_configuration
