from __future__ import unicode_literals
import re
import copy
import logging

from modularodm import Q
from modularodm.exceptions import NoResultsFound
from modularodm.storage.base import KeyExistsException

from framework.mongo import database
from framework.transactions.context import TokuTransaction

from scripts import utils as scripts_utils

from website.app import init_app
from website.project.model import NodeLog
from website.addons.osfstorage import model, oldels

logger = logging.getLogger(__name__)


def migrate_download_counts(node, old, new, dry=True):
    if dry:
        new_id = ':'.join(['download', node._id, 'new id'])
    else:
        new_id = ':'.join(['download', node._id, new._id])

    old_id = ':'.join(['download', node._id, old.path.replace('.', '_').replace('$', '_')])
    escaped_id = ':'.join(['download', node._id, re.escape(old.path.replace('.', '_').replace('$', '_'))])

    for doc in database.pagecounters.find({'_id': {'$regex': '^{}(:\d)?'.format(escaped_id)}}):
        new_doc = copy.deepcopy(doc)
        assert old_id in doc['_id']
        new_doc['_id'] = doc['_id'].replace(old_id, new_id)

        logger.info('{} -> {}'.format(doc, new_doc))
        if not dry:
            database.pagecounters.insert(new_doc)
            database.pagecounters.remove(doc)


def migrate_node_settings(node_settings, dry=True):
    logger.info('Running `on add` for node settings of {}'.format(node_settings.owner._id))

    if not dry:
        node_settings.on_add()

def migrate_file(node, old, parent, dry=True):
    assert isinstance(old, oldels.OsfStorageFileRecord)
    logger.debug('Creating new child {}'.format(old.name))
    if not dry:
        try:
            new = parent.append_file(old.name)
        except KeyExistsException:
            logger.warning('{!r} has already been migrated'.format(old))
            return
        new.versions = old.versions
        new.is_deleted = old.is_deleted
        new.save()
    else:
        new = None

    migrate_guid(node, old, new, dry=dry)
    migrate_log(node, old, new, dry=dry)
    migrate_download_counts(node, old, new, dry=dry)


def migrate_log(node, old, new, dry=True):
    res = NodeLog.find(
        (
            Q('params.node', 'eq', node._id) |
            Q('params.project', 'eq', node._id)
        ) &
        Q('params.path', 'eq', old.path) &
        Q('action', 'istartswith', 'osf_storage_file')
    )

    if res.count():
        logger.info('Migrating {} logs for {!r} in {!r}'.format(res.count(), old, node))
    else:
        logger.debug('No logs to migrate for {!r} in {!r}'.format(res.count(), old, node))

    for log in res:
        if dry:
            logger.debug('{!r} {} -> {}'.format(log, log.params['path'], 'New path'))
        else:
            logger.debug('{!r} {} -> {}'.format(log, log.params['path'], new.materialized_path()))
            log.params['path'] = new.materialized_path()
            url = node.web_url_for(
                'addon_view_or_download_file',
                path=new.path.strip('/'),
                provider='osfstorage'
            )
            log.params['urls'] = {
                'view': url,
                'download': url + '?action=download'
            }
            log.save()


def migrate_guid(node, old, new, dry=True):
    try:
        guid = model.OsfStorageGuidFile.find_one(
            Q('node', 'eq', node) &
            Q('path', 'eq', old.path)
        )
        logger.info('Migrating file guid {}'.format(guid._id))
    except NoResultsFound:
        logger.debug('No guids found for {}'.format(old.path))
        return

    if not dry:
        guid.path = new.path
        guid.save()

def migrate_children(node_settings, dry=True):
    if not node_settings.file_tree:
        logger.warning('Skipping node {}; file_tree is None'.format(node_settings.owner._id))
        return

    logger.info('Migrating children of node {}'.format(node_settings.owner._id))
    for child in node_settings.file_tree.children:
        migrate_file(node_settings.owner, child, node_settings.root_node, dry=dry)


def main(dry=True):
    if not dry:
        scripts_utils.add_file_logger(logger, __file__)
        logger.info('Not running in dry mode, changes WILL be made')
    else:
        logger.info('Running in dry mode, changes NOT will be made')

    for node_settings in model.OsfStorageNodeSettings.find():
        try:
            with TokuTransaction():
                migrate_node_settings(node_settings, dry=dry)
                migrate_children(node_settings, dry=dry)
        except Exception as error:
            logger.error('Could no migrate file tree from {}'.format(node_settings.owner._id))
            logger.exception(error)


if __name__ == '__main__':
    import sys
    dry = 'dry' in sys.argv

    if 'debug' in sys.argv:
        logger.setLevel(logging.DEBUG)
    elif 'info' in sys.argv:
        logger.setLevel(logging.INFO)
    elif 'error' in sys.argv:
        logger.setLevel(logging.ERROR)

    with init_app(set_backends=True, routes=True).test_request_context():
        main(dry=dry)

@@ -560,24 +560,93 @@
  dry=True):%0A
+    escaped_old_path = old.path.replace('.', '_').replace('$', '_')%0A%0A
     if dry:%0A
@@ -824,52 +824,24 @@
 id, 
+escaped_
 old
-.
+_
 path
-.replace('.', '_').replace('$', '_')
 %5D)%0A 
@@ -902,52 +902,24 @@
 ape(
+escaped_
 old
-.
+_
 path
-.replace('.', '_').replace('$', '_')
 )%5D)%0A
@@ -988,16 +988,17 @@
 '%5E%7B%7D(:%5Cd
++
 )?'.form
@@ -1089,16 +1089,63 @@
 %5B'_id'%5D%0A
+        if len(doc%5B'_id'%5D.split(':')) %3C 4:%0A    
         
@@ -1196,36 +1196,340 @@
 new_id)%0A
-%0A        logger.info
+        else:%0A            version = int(doc%5B'_id'%5D.split(':')%5B-1%5D)%0A            assert version %3E 0%0A            logger.debug('Decrementing version %7B%7D to %7B%7D'.format(version, version - 1))%0A            new_doc%5B'_id'%5D = doc%5B'_id'%5D.replace('%7B%7D:%7B%7D'.format(old_id, version), '%7B%7D:%7B%7D'.format(new_id, version - 1))%0A%0A        logger.debug
 ('%7B%7D -%3E 
@@ -1668,16 +1668,23 @@
 move(doc
+%5B'_id'%5D
 )%0A%0A%0Adef 

Fix migrate_download_counts