import asyncio
import codecs
import csv
from datetime import datetime
import glob
import logging
import logging.config
import os
import shutil
import time

import aioeasywebdav

import common

# Folder to download files to
LOCAL_DOWNLOAD_DIR = "/ftproot/Intersport_Stammdaten"
# Folder to presort files by ILN in
LOCAL_PRESORT_DIR = "/ftproot/Intersport_Stammdaten" # /home/holger/tmp/Intersport
# File contains the last run timestamp
LAST_DOWNLOAD_DATE_FILENAME = "intersport_last_download.txt"
# Name of the file containing the ILNs
CATALOG_INDEX_FILENAME = "catalogModelSearch.idx.txt"
# URL to catalog data
DAV_URL = "https://portal.intersport.de/irj/go/km/docs/Stammdaten/Kataloge"

# Non-master M24531 z03b@GYn5$x8

# Login name
DAV_USR = "M12735"
# Login password
DAV_PWD = "ct741369"

MTIME_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S %Z"
ISO_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"


current_files = None
webdav = None

log_file = os.path.join(LOCAL_DOWNLOAD_DIR, __file__ + ".log")
logging.config.dictConfig(common.get_logging_config(log_file))

logger = logging.getLogger(__file__)


def read_last_download_date():
    """ Read last download date from file. 
    
        Returns:
            last date from file or 1/1/2010
    """
    fn = os.path.join(LOCAL_DOWNLOAD_DIR, LAST_DOWNLOAD_DATE_FILENAME)
    if os.path.isfile(fn):
        with open(fn, 'r') as f:
            last_download_date_text = f.readline()

        try:
            last_download_date = datetime.strptime(last_download_date_text,
                                                   ISO_DATE_FORMAT)
        except Exception as ex:
            logger.error(ex)
            last_download_date = datetime(2010,1,1)

        return last_download_date

    return datetime(2010, 1, 1)

def write_last_download_date(download_date: datetime):
    """ Write last download date to file in ISO format
    
        Args:
            download_date:  Date&time the last download started

        Returns:
            bool:   True if successfully written. False otherwise
    """
    try:
        fn = os.path.join(LOCAL_DOWNLOAD_DIR, LAST_DOWNLOAD_DATE_FILENAME)	
        with open(fn, 'w') as of:
            of.write(download_date.isoformat())

        return True
    except Exception as ex:
        logger.error(ex)

    return False

def is_zip_file(filename):
    """Filter out zip files"""
    return len(filename) > 5 and filename[-4:].lower() == ".zip"

def parse_index_file(index_file):
    """ Parse the index file and return a reversed dictionary that maps filename
        to ILN. In the example below, 'COFS18' maps to '0008949350019'.

        Example Index File Content
            INDEX|v1.0|20180827200221
            ILN|0008949350019
            CAT|0|COFS18|691
            CAT|1|COFS19|567
            CAT|2|COHW18|889
            ILN|0022869000007
            ...
    """
    current_iln = None
    iln_dict = dict()
    if os.path.isfile(index_file):
        is_done =False
        with codecs.open(index_file, 'r', encoding='cp1252', errors="replace") as f:
            while f and not is_done:
                l = f.readline()
                l = l.replace("\r","").replace("\n","")
                arr = l.split("|")
                if arr:
                    if arr[0] == "ILN" and len(arr) >= 2:
                        current_iln = arr[1]
                    elif arr[0] == "CAT" and len(arr) >= 3:
                        filename = arr[2]
                        if filename in iln_dict:
                            iln_dict[filename].append(current_iln)
                        else:
                            iln_dict[filename] = [current_iln]
                    elif arr[0] == "BRN":
                        is_done = True
    
    return iln_dict

def presort_files(download_dir, presort_base_dir, local_index_file):
    """ Sort files into their ILN folders"""
    if os.path.isfile(local_index_file):
        presort_dict = parse_index_file(local_index_file)

    iln_found = 0
    iln_not_found = 0
    for zip_file in glob.glob(os.path.join(download_dir, "*.zip")):
        zip_filename = os.path.basename(zip_file)
        filename, _ = os.path.splitext(zip_filename)
        filename_upper = filename.upper()
        zip_file_path = os.path.join(download_dir, zip_file)
        if filename_upper in presort_dict:
            iln_found += 1
            iln = presort_dict[filename_upper]
            if len(iln) > 1:
                logger.info("File {} has more than 1 ILN {}".format(filename_upper, iln))
            presort_dir  = os.path.join(presort_base_dir, iln[0])
            try:
                if not os.path.exists(presort_dir):
                    os.makedirs(presort_dir)
                presort_file_path = os.path.join(presort_dir, zip_filename)

                # Delete any existing files
                if os.path.isfile(presort_file_path):
                    os.remove(presort_file_path)
				
                logger.debug("Presorting %s into %s", zip_file_path, presort_file_path)			
                shutil.move(zip_file_path, presort_file_path)
            except Exception as ex:
                logger.error(ex)
        else:
            iln_not_found += 1
            logger.info("File {} has no ILN".format(zip_file_path))
			
    logger.info("Found {} / Not found {}".format(iln_found, iln_not_found))

async def download_file(remote_file, local_file):
    """ Async file download """
    try:
        await webdav.download(remote_file, local_file)
    except Exception as ex:
        logger.error(ex)


async def get_new_files_on_server_list(start_date: datetime):
    """ Download a list of all files and return only those of interest that are 
        newer than the last download time."""

    files_on_server = list()
    try:
        files_on_server = await webdav.ls()
    except Exception as ex:
        logger.error(ex)

    filtered_file_list = list()

    for f in files_on_server:
        if not is_zip_file(f.name):
            logger.info("Skipping {}".format(f.name))
            continue

        file_time = datetime.strptime(f.mtime, MTIME_DATE_FORMAT)

        if file_time >= start_date:
            filtered_file_list.append(f.name)

    return filtered_file_list


def main():
    global logger, webdav

    now = datetime.now()

    last_download_date = read_last_download_date()
    logger.info("Last run time was {}".format(last_download_date.isoformat()))

    loop = asyncio.get_event_loop()
    webdav = aioeasywebdav.connect(DAV_URL, username=DAV_USR, password=DAV_PWD)

    new_file_list = loop.run_until_complete(
        get_new_files_on_server_list(last_download_date))

    logger.info("Retrieved {} file names from site.".format(
        len(new_file_list)))

    tasks = list()
    local_index_file = os.path.join(LOCAL_DOWNLOAD_DIR, CATALOG_INDEX_FILENAME)
    # Always download the index file
    remote_index_file = "/" + CATALOG_INDEX_FILENAME
    logger.info("Downloading Index File from {} to {}...".format(remote_index_file, local_index_file))
    tasks.append(asyncio.ensure_future(download_file(
        remote_index_file, local_index_file)))

    logger.info("Downloading files...")
    for new_file in new_file_list:
        local_file = os.path.join(LOCAL_DOWNLOAD_DIR,
                                  os.path.basename(new_file))
        tasks.append(
            asyncio.ensure_future(download_file(new_file, local_file)))

    loop.run_until_complete(asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED))
    logger.info("Download finished")
	
    presort_files(LOCAL_DOWNLOAD_DIR, LOCAL_DOWNLOAD_DIR, local_index_file)

    write_last_download_date(now)

    logger.info("Done.")

if __name__ == "__main__":
    main()
