#!/usr/bin/env python

import os
import sys
import logging
import boto.s3
import bagit
import shutil
from tempfile import mkdtemp

from umobj.utils import umobj_logging, umobj_init, \
    umobj_get_bucket_key_pair_from_string
from umobj.verify import obj_checksums, load_bag_checksums
from umobj.tar import make_tarfile_from_files
from umobj.transfer import obj_upload, obj_key
from umobj.email_utils import send_email
from umobj.obj import Obj
from umobj.options import umobj_parser, get_logging_level
from qav.questions import Question, QuestionSet
from qav.validators import EmailValidator

body_text = '''
This is a report of the following BagIt run.  Please find attached the
manifest from the job that just completed.

Details
=======
Source Directory: %(source)s
Destination: %(dest)s
Contact Name: %(Contact-Name)s
Contact Email: %(Contact-Email)s
Description: %(External-Description)s

'''

pbar = None


def diff(a, b):
    b = set(b)
    return [aa for aa in a if aa not in b]

if __name__ == "__main__":
    umobj_init()

    description = 'BagIt'
    parser = umobj_parser(description=description)
    parser.add_multipart()
    parser.add_s3path(name='SOURCE', number=1,
                      help='Source Directory')
    parser.add_s3path(name='DESTINATION', number=1,
                      help='Destination Bucket')
    args = parser.parse_args()

    umobj_logging(get_logging_level(args))  # set up logging

    logging.info("Running %s" % sys.argv)

    if not Obj.connect(host=args.host,
                       port=args.port,
                       access_key=args.access_key,
                       secret_key=args.secret_key):
        logging.error('Unable to contact object store.')
        sys.exit(1)

    src = os.path.normpath(args.SOURCE[0])
    dest = args.DESTINATION[0]
    dest_bucket_name, dest_key_name = \
        umobj_get_bucket_key_pair_from_string(dest)
    logging.debug("DEST Bucket Name: %s" % dest_bucket_name)
    logging.debug("DEST Key Name: %s" % dest_key_name)
    bucket_name = dest_bucket_name

    try:
        bucket = Obj.conn.get_bucket(bucket_name)
    except boto.exception.S3ResponseError as e:
        logging.error("Unable to access bucket %s, %s." %
                      (bucket_name, e.error_code))
        sys.exit(1)

    try:
        bag = bagit.Bag(src)
        logging.info('Bag %s already exists; not rebaging.' % src)
        bag_info = bag.info
    except bagit.BagError:
        qset = QuestionSet()
        qset.add(Question('Contact Name', 'Contact-Name'))
        qset.add(Question('Contact Email', 'Contact-Email', EmailValidator()))
        qset.add(Question('Description', 'External-Description'))

        qset.ask()
        bag_info = qset.answers
        logging.info('bag_info %s' % bag_info)
        bag = bagit.make_bag(src, bag_info)

    basename = os.path.basename(os.path.abspath(src))

    if dest_key_name is None:
        key_path = src.split(os.sep)[-1]
    else:
        key_path = '%s/%s' % (dest_key_name, src.split(os.sep)[-1])

    bagit_txt = obj_key(bucket_name, '%s/bagit.txt' % key_path)
    if bagit_txt is not None:   # bag may already exist
        obj_entries = load_bag_checksums(bucket_name, key_path)
        if cmp(obj_entries, bag.entries) != 0:
            logging.error('Conflict between manifest in filesystem and ' +
                          'object store.  Please remove or rename the bag ' +
                          'in the object store before trying to continue.')
            sys.exit(1)
        logging.info('Bag is already uploaded.  You can do a full ' +
                     'validation with the cmpobj command.')
    else:  # bag does not appear to exist
        obj_upload(bucket_name, src, dest_key_name, True, args.multipart)
        print 'Bag successfully uploaded to object store.'

        tmp_path = mkdtemp(prefix='umobj_bagit-', dir='/tmp')
        manifest_filename = '%s/manifest' % tmp_path
        bagit_filename = '%s/bagit.txt' % tmp_path
        bagit_key = obj_key(bucket_name, '%s/bagit.txt' % key_path)
        bag_info_filename = '%s/bag-info.txt' % tmp_path
        bag_info_key = obj_key(bucket_name, '%s/bag-info.txt' % key_path)

        archive_filenames = [manifest_filename,
                             bagit_filename, bag_info_filename]

        compressed_manifest_filename = '%s/manifests.tar.gz' % tmp_path

        algo, uncompressed_manifest_content = obj_checksums(bucket_name,
                                                            key_path)

        # write stored manifest out to a tmp file
        with open(manifest_filename, 'w') as f:
            f.write(uncompressed_manifest_content)

        # write bagit.txt out to a tmp file
        with open(bagit_filename, 'w') as f:
            f.write(bagit_key.get_contents_as_string())

        # write bag-info.txt out to a tmp file
        with open(bag_info_filename, 'w') as f:
            f.write(bag_info_key.get_contents_as_string())

        make_tarfile_from_files(compressed_manifest_filename,
                                archive_filenames,
                                squash_hierarchy=True)

        body_text_info = dict(bag_info)
        body_text_info['source'] = src
        body_text_info['dest'] = dest

        # email the manifests
        send_email(send_from='bagobj@umiacs.umd.edu',
                   send_to=[bag_info['Contact-Email']],
                   subject='Bagobj report for %s' % os.path.abspath(src),
                   body_text=body_text % body_text_info,
                   files=[compressed_manifest_filename])

        # delete the temporary files/directory
        shutil.rmtree(tmp_path)

        print 'Please verify the content and you may, ' + \
              'when confident, remove the local ' + \
              'filesystem directory: %s.' % os.path.abspath(src)
