#!/usr/bin/env python3
# pylint: disable=W0703
"""
    cij_fetch - Sync from remote reservoir to local copy

    Uses .md5 to skip downloads when local copy matches remote.
"""
from __future__ import print_function
from subprocess import Popen, PIPE
import argparse
import sys
import os
try:
    from urllib.parse import urlparse
    from urllib.request import urlopen
except ImportError:
    from urlparse import urlparse
    from urllib2 import urlopen

CHUNK_SIZE = 16 * 1024


def md5_create(path):
    """
    Create md5sum of <path> and store it in <path>.md5
    Returns returncode from Popen != 0 => error
    """

    cmd = ['md5sum', path]

    rcode = 0
    with Popen(cmd, stdout=PIPE, stderr=PIPE) as process:
        out, _ = process.communicate()

        if process.returncode == 0:
            with open(f"{path}.md5", 'w', encoding="UTF-8") as md5_fd:
                md5_fd.write(out)

        rcode = process.returncode

    return rcode


def meta_local(path):
    """Equivalent of meta_reservoir but on local filesystem"""

    length = None
    try:
        length = os.path.getsize(path)
    except Exception as exc:
        _, _, exc_tb = sys.exc_info()
        print("Around lineno(%s) error(%s)" % (exc_tb.tb_lineno, exc))

    md5 = None
    try:
        with open("%s.md5" % path, encoding="UTF-8") as md5_fd:
            md5, _, _ = md5_fd.read().split(" ")
    except Exception as exc:
        _, _, exc_tb = sys.exc_info()
        print("Around lineno(%s) error(%s)" % (exc_tb.tb_lineno, exc))

    if length:
        return {"length": length, "md5": md5}

    return None


def meta_reservoir(url):
    """Returns {"length": ..., "md5": ...} on url in reservoir"""

    length = None
    try:
        length = int(urlopen(url).info().getheaders("Content-Length")[0])
    except Exception as exc:
        _, _, exc_tb = sys.exc_info()
        print("Around lineno(%s) error(%s)" % (exc_tb.tb_lineno, exc))

    md5 = None
    try:
        md5, _, _ = reservoir_to_mem("%s.md5" % url).split(" ")
    except Exception as exc:
        _, _, exc_tb = sys.exc_info()
        print("Around lineno(%s) error(%s)" % (exc_tb.tb_lineno, exc))

    if length:
        return {"length": length, "md5": md5}

    return None


def meta_is_equiv(one, other):
    """Return True two different metas has the same size and md5sum."""

    return (
        (one is not None and other is not None) and
        (one["length"] == other["length"]) and
        (one["md5"] == other["md5"])
    )


def reservoir_to_mem(url):
    """Returns reservoir resource from url."""

    resource = urlopen(url)
    content = resource.read()

    return content


def reservoir_to_file(url, path):
    """Downloads the url to path"""

    url_meta = meta_reservoir(url)
    local_meta = meta_local(path)

    if meta_is_equiv(local_meta, url_meta):     # Skip if it meta is equivalent
        return True

    local_dir = os.path.dirname(path)           # Ensure parent dir exists
    if not os.path.exists(local_dir):
        os.makedirs(local_dir)

    try:
        response = urlopen(url)                 # Download the file
        with open(path, 'wb') as res_fd:
            while True:
                chunk = response.read(CHUNK_SIZE)
                if not chunk:
                    break
                res_fd.write(chunk)

        md5_create(path)                        # Create md5sum
        local_meta = meta_local(path)           # Refresh

    except Exception as exc:
        _, _, exc_tb = sys.exc_info()
        print("Around lineno(%s) error(%s)" % (exc_tb.tb_lineno, exc))

    return meta_is_equiv(local_meta, url_meta)


def main(args):
    """
    Main entry-point of cij_fetch.

    Set exit-code to 0 on success and 1 on failure
    """

    url = args.url                          # Grab url information
    url_parts = urlparse.urlparse(args.url)
    url_path = url_parts.path

    local_path = os.path.normpath(os.path.join(args.root, url_path))

    if reservoir_to_file(url, local_path):
        sys.exit(0)
    else:
        sys.exit(1)


if __name__ == "__main__":
    PARSER = argparse.ArgumentParser(
        description='cij_fetch - Fetches file from reservoir'
    )
    PARSER.add_argument(
        'root', help="Path to root of local reservoir"
    )
    PARSER.add_argument(
        'url', help="Url to reservoir resource"
    )
    ARGS = PARSER.parse_args()

    main(ARGS)
