#!/usr/bin/env python
"""
Renames a session and its experiment.

Usage:
    dm_xnat_rename.py [options] <prev_name> <new_name>
    dm_xnat_rename.py [options] <name_file>

Arguments:
    <prev_name>     The current experiment name on XNAT
    <new_name>      The new experiment name
    <name_file>     The full path to a csv file of sessions to rename. Each
                    entry for a session should be formatted as
                    "current_name,new_name", one entry per line.

Options:
    --server,-s <server>        The URL of the xnat server to rename a session
                                on. If unset, it will be read from the
                                'XNATSERVER' environment var

    --user,-u <user>            The username to log in with. If unset, it will
                                be read from the 'XNAT_USER' environment var.

    --pass,-p <pass>            The password to log in with. If unset it will
                                be read from the 'XNAT_PASS' environment var.

    --project xnat_project      Limit the rename to the given XNAT project

    --debug, -d
    --verbose, -v
    --quiet, -q
"""
import os
import logging
from docopt import docopt

from requests import HTTPError

import datman.xnat
import datman.config
import datman.scanid
from datman.exceptions import XnatException, ParseException

logging.basicConfig(level=logging.WARN,
                    format="[%(name)s] %(levelname)s: %(message)s")
logger = logging.getLogger(os.path.basename(__file__))


def main():
    arguments = docopt(__doc__)
    name_path = arguments['<name_file>']
    source = arguments['<prev_name>']
    dest = arguments['<new_name>']
    server = arguments['--server']
    user = arguments['--user']
    password = arguments['--pass']
    project = arguments['--project']

    set_log_level(arguments)

    config = datman.config.config()
    xnat = datman.xnat.get_connection(
        config, url=server, auth=(user, password))

    if not name_path:
        rename_xnat_session(xnat, source, dest, project=project)
        return

    names = read_sessions(name_path)
    for old_name, new_name in names:
        try:
            rename_xnat_session(xnat, old_name, new_name, project=project)
        except Exception as e:
            logger.error("Failed to rename {} to {}. Reason - "
                         "{}".format(old_name, new_name, e))


def set_log_level(arguments):
    debug = arguments['--debug']
    verbose = arguments['--verbose']
    quiet = arguments['--quiet']

    # The xnat module is noisy, its default level should be 'error'
    xnat_logger = logging.getLogger("datman.xnat")
    xnat_logger.setLevel(logging.ERROR)

    if debug:
        logger.setLevel(logging.DEBUG)
        xnat_logger.setLevel(logging.DEBUG)
    if verbose:
        logger.setLevel(logging.INFO)
        xnat_logger.setLevel(logging.INFO)
    if quiet:
        logger.setLevel(logging.ERROR)


def read_sessions(name_file):
    with open(name_file, "r") as name_list:
        lines = name_list.readlines()

    entries = []
    for entry in lines:
        fields = entry.split(",")
        if len(fields) != 2:
            logger.error("Invalid entry found: {}. Ignoring".format(entry))
            continue
        entries.append([field.strip() for field in fields])
    logger.debug("Found {} valid entries".format(len(entries)))
    return entries


def rename_xnat_session(xnat, orig_name, new_name, project=None):
    """Rename a session on XNAT.

    Args:
        xnat (:obj:`datman.xnat.xnat`): A connection to the XNAT server.
        orig_name (:obj:`str`): The current session name on XNAT.
        new_name (:obj:`str`): The new name to apply to the session.
        project (:obj:`str`, optional): The XNAT project the session belongs
            to. If not given, it will be guessed based on orig_name. Defaults
            to None.

    Raises:
        XnatException: If problems internal to
            :obj:`datman.xnat.xnat.rename_session` occur.
        requests.HTTPError: If XNAT's API reports issues.

    Returns:
        bool: True if rename succeeded, False otherwise
    """
    if not project:
        project = get_project(orig_name)

    try:
        ident = datman.scanid.parse(new_name)
    except ParseException:
        raise ParseException("New ID {} for experiment {} doesnt match a "
                             "supported naming convention.".format(
                                 new_name, orig_name))

    logger.info("Renaming {} to {} in project {}".format(orig_name, new_name,
                                                         project))

    orig_subject = xnat.find_subject(project, orig_name)
    try:
        xnat.rename_subject(project, orig_subject, ident.get_xnat_subject_id())
    except HTTPError as e:
        if e.response.status_code == 500:
            # This happens on success sometimes (usually when experiment
            # is empty). Check if the rename succeeded.
            try:
                xnat.get_subject(project, ident.get_xnat_subject_id())
            except XnatException:
                raise e

    xnat.rename_experiment(project, ident.get_xnat_subject_id(),
                           orig_name, ident.get_xnat_experiment_id())


def get_project(session):
    config = datman.config.config()
    try:
        project = config.map_xnat_archive_to_project(session)
    except ParseException as e:
        raise ParseException(
            "Can't guess the XNAT Archive for {}. Reason - {}. Please provide "
            "an XNAT Archive name with the --project option".format(
                session, e))
    except Exception as e:
        raise type(e)("Can't determine XNAT Archive for {}. Reason - {}"
                      "".format(session, e))
    return project


if __name__ == "__main__":
    main()