#!/usr/bin/env python
from logging.handlers import SysLogHandler
from os.path import basename, expanduser, join as path_join, realpath
from netrc import netrc
from tempfile import gettempdir
import argparse
import hashlib
import json
import logging
import re
import signal
import sys

from lockfile import LockFile
from sh import lftp
import requests
import sh

from xirvik.logging import get_logger
from xirvik.util import ctrl_c_handler, cleanup_and_exit
from xirvik.client import ruTorrentClient
import xirvik.client


TORRENT_PATH_INDEX = xirvik.client.TORRENT_PATH_INDEX
_lock = None

def lock_ctrl_c_handler(signum, frame):
    if _lock:
        _lock.release()

    ctrl_c_handler(signum, frame)
    raise SystemExit('Signal raised')

if __name__ == '__main__':
    signal.signal(signal.SIGINT, lock_ctrl_c_handler)

    parser = argparse.ArgumentParser()

    parser.add_argument('-H', '--host', required=True)
    parser.add_argument('-P', '--port', type=int, default=22)
    parser.add_argument('-c', '--netrc-path', default=expanduser('~/.netrc'))
    parser.add_argument('-T', '--move-to', required=True)
    parser.add_argument('-L', '--label', default='Seeding')
    parser.add_argument('-d', '--debug', action='store_true')
    parser.add_argument('-v', '--verbose', action='store_true')
    parser.add_argument('-s', '--syslog', action='store_true')
    parser.add_argument('remote_dir', metavar='REMOTEDIR', nargs=1)
    parser.add_argument('local_dir', metavar='LOCALDIR', nargs=1)

    args = parser.parse_args()
    verbose = args.debug or args.verbose
    log = get_logger('xirvik-mirror',
                     verbose=args.verbose,
                     debug=args.debug,
                     syslog=args.syslog)
    if args.debug:
        sh.logging_enabled = True
        logs_to_follow = (
            'requests.packages.urllib3',
            'command',
            xirvik.client.LOG_NAME,
        )
        for name in logs_to_follow:
            _log = logging.getLogger(name)
            formatter = logging.Formatter('%(asctime)s - %(name)s - '
                                          '%(levelname)s - %(message)s')
            channel = logging.StreamHandler(sys.stderr)

            _log.setLevel(logging.DEBUG)
            channel.setLevel(logging.DEBUG)
            channel.setFormatter(formatter)
            _log.addHandler(channel)

    local_dir = realpath(args.local_dir[0])
    user, _, password = netrc(args.netrc_path).authenticators(args.host)
    sftp_host = 'sftp://{user:s}@{host:s}'.format(
        user=user,
        host=args.host,
    )

    lf_hash = hashlib.sha256(json.dumps(args._get_kwargs()).encode('utf-8')).hexdigest()
    lf_path = path_join(gettempdir(), 'xirvik-mirror-{}'.format(lf_hash))
    log.debug('Acquiring lock at {}'.format(lf_path))
    _lock = LockFile(lf_path)
    _lock.acquire()
    log.debug('Lock acquired')

    log.debug('Local directory to sync to: {}'.format(local_dir))
    log.debug('Read user and password from netrc file')
    log.debug('SFTP URI: {}'.format(sftp_host))

    client = ruTorrentClient(args.host, user, password)

    http_prefix = 'https://{host:s}'.format(host=args.host)
    multirpc_action_uri = ('{}/rtorrent/plugins/multirpc/'
                           'action.php'.format(http_prefix))
    datadir_action_uri = ('{}/rtorrent/plugins/datadir/'
                          'action.php'.format(http_prefix))
    assumed_path_prefix = '/torrents/{}'.format(user)
    look_for = '{}/{}/'.format(assumed_path_prefix, args.remote_dir[0])
    move_to = '{}/{}'.format(assumed_path_prefix, args.move_to)
    names = {}

    log.debug('Full completed directory path name: {}'.format(look_for))
    log.debug('Moving finished torrents to: {}'.format(move_to))

    log.info('Getting current torrent information (ruTorrent)')
    for hash, v in client.list_torrents().items():
        if not v[TORRENT_PATH_INDEX].startswith(look_for):
            continue
        bn = basename(v[TORRENT_PATH_INDEX])
        names[bn] = (hash, v[TORRENT_PATH_INDEX],)

        log.info('Completed torrent "{}" found with hash {}'.format(bn, hash,))

    log.info('Verifying contents of {} with previous '
             'response'.format(look_for))
    lftp_e = '; '.join([
        'set color:use-color false',
        'cd {}'.format(args.remote_dir[0]),
        'ls',
        'exit',
    ])
    ls_regex = re.compile(r'^[\-d](?:[\-r][\-w][\-x]){3}\s+[a-zA-Z0-9]+'
                          '/[a-zA-Z0-9]+\s+\d+\s+[0-9]{4}\-[0-9]{2}\-[0-9]{2}'
                          '\s[0-9]{2}\:[0-9]{2}\:[0-9]{2}\s+(.*)$')
    ls_out = lftp(['-p', args.port,
                   '-e', lftp_e,
                   sftp_host]).strip().split('\n')

    for line in ls_out:
        parsed = ls_regex.match(line)
        if not parsed:
            log.error('Could not parse line: {}'.format(line))
            continue
        name = parsed.group(1)

        if name == '..' or name == '.':
            continue

        if name not in names:
            log.error('File or directory "{}" not found in previous '
                      'response body'.format(name))
            continue

        log.debug('Found matching torrent "{}" from lftp ls '
                  'output'.format(name))

    if not len(names.items()):
        log.info('Nothing found to mirror')
        _lock.release()
        cleanup_and_exit()

    # NOTE This could be done with paramiko but later
    lftp_e = '; '.join([
        'set color:use-color false',
        'lcd {}'.format(local_dir),
        'cd {}'.format(args.remote_dir[0]),
        'mirror',
        'exit',
    ])
    log.info('Mirroring {}{} to {}'.format(sftp_host, look_for, local_dir))
    lftp(['-p', args.port, '-e', lftp_e, sftp_host])

    # Move to _seeding directory and set label
    for bn, (hash, fullpath) in names.items():
        log.info('Moving "{}" to "{}" directory'.format(bn, move_to))
        client.move_torrent(hash, move_to)

        log.info('Setting label to "{}" for "{}"'.format(args.label, bn))
        client.set_label(hash, args.label)

    _lock.release()
    cleanup_and_exit()
