#!/usr/bin/env python
# coding=utf-8
from filechunkio import FileChunkIO
from boto.exception import S3ResponseError
from boto.s3.connection import S3Connection, OrdinaryCallingFormat
from datetime import date, timedelta
from os import listdir, path, stat
from sys import argv
import logging
from platform import node
from math import ceil

try:
    from ConfigParser import (SafeConfigParser as ConfigParser,
                              Error as parseError)
except ImportError:
    from configparser import ConfigParser, Error as parseError


def get_map_key(filename):
    return filename[:filename.rfind('.', 0, filename.rfind('.'))].lower()


class S3Pusher(object):
    def __init__(self, config):
        self.hostname = node()
        self.suffix = ".gz"
        self.key_suffix = ".gz"
        self.directory = "."
        self.today = date.today()
        self.bucket = None
        self.map = {}
        self.chunk_size = 52428800
        self.connect(config)

    def get_filename(self, filename):
        index = int(filename[filename.rfind('.', 0, filename.rfind('.')) + 1:
                             filename.rfind('.')])
        return (
            self.today - timedelta(days=1 + index)
        ).strftime("%Y-%m-%d") + self.key_suffix

    def connect(self, config):
        configs = ConfigParser()
        try:
            configs.read(config)
            logging.debug("conf (%s) parsed" % configs)
            self.directory = configs.get("logs", "directory")
            self.suffix = configs.get("logs", "suffix")
            if configs.has_option("S3", "chunk_size"):
                self.chunk_size = configs.getint("S3", "chunk_size")
            if configs.has_option("logs", "key_suffix"):
                self.key_suffix = configs.get("logs", "key_suffix")
            self.map = dict(configs.items("map"))
            conn = S3Connection(
                aws_access_key_id=configs.get("S3", "access_key"),
                aws_secret_access_key=configs.get("S3", "secret_key"),
                host=configs.get("S3", "host"),
                calling_format=OrdinaryCallingFormat())
            bucket_name = configs.get("S3", "bucket")
            try:
                bucket = conn.get_bucket(bucket_name)
                self.bucket = bucket
            except S3ResponseError:
                logging.warning("Bucket %s not found. Creating." % bucket_name)
                bucket = conn.create_bucket(bucket_name)
                self.bucket = bucket

            logging.info("Using bucket %s" % self.bucket.name)

        except parseError as e:
            logging.error("Bad config file: %s" % e)
            exit(1)

    def list_candidates(self):
        candidates = [filename.lower() for filename in listdir(self.directory)
                      if filename.endswith(self.suffix) and
                      get_map_key(filename) in self.map]
        logging.info("Found %s candidates at %s"
                     % (len(candidates), self.directory))
        return candidates

    def push_file(self, filename):
        key_name = path.join(
            self.hostname,
            self.map[get_map_key(filename)],
            self.get_filename(filename)
        )
        if self.bucket.get_key(key_name):
            logging.debug("%s exists" % filename)
            return

        key = self.bucket.initiate_multipart_upload(key_name)
        source_path = path.join(self.directory, filename)
        source_size = stat(source_path).st_size
        chunk_count = int(ceil(source_size / float(self.chunk_size)))
        logging.debug("%s uploading as %s. %d chunks"
                      % (filename, key_name, chunk_count))
        for i in range(chunk_count):
            offset = self.chunk_size * i
            amount = min(self.chunk_size, source_size - offset)
            with FileChunkIO(source_path, 'r',
                             offset=offset, bytes=amount) as fp:
                key.upload_part_from_file(fp, part_num=i + 1)

        key.complete_upload()
        logging.debug("%s uploaded" % filename)

    def push_candidates(self):
        for filename in self.list_candidates():
            self.push_file(filename)


if __name__ == '__main__':
    if len(argv) > 1:
        if len(argv) > 2:
            if argv[1] == "-v":
                pusher = S3Pusher(argv[2])
                logging.getLogger().setLevel(logging.DEBUG)
            elif argv[2] == "-v":
                pusher = S3Pusher(argv[1])
                logging.getLogger().setLevel(logging.DEBUG)
            else:
                pusher = None
                logging.error("Usage %s [-v] config_file.conf" % argv[0])
                exit(2)
        else:
            pusher = S3Pusher(argv[1])

        pusher.push_candidates()
    else:
        logging.error("Usage %s [-v] config_file.conf" % argv[0])
        exit(1)
