from assembla.error import AssemblaError
from assembla.models import Ticket
import logging
import re
from copy import deepcopy

logger = logging.getLogger('ATMT')

def prettify(changes):
    if isinstance(changes,str) or isinstance(changes,unicode):
        changes = changes.replace('---','*Emulated Ticket Change*', 1)
        changes = changes.replace('\n- -','\n*Field:*')
        before = changes
        while True:
            after = before.replace('\n  -','\n_From:_', 1)
            after = after.replace('\n  -','\n_To:_', 1)
            if before == after:
                return after
            before = after
    return changes

def remap_references(line, number_map):
    if isinstance(line, str) or isinstance(line, unicode):
        refs = {int(ref) for ref in re.findall(r'#([0-9]+)', line)}
        for r in refs:
            nm = number_map.get(r, 'not-copied')
            line = line.replace('#%s' % r, '#%s' % nm)
    else:
        logger.debug('[ReferenceRemap] Skipping unknown type: %s, line: %s', type(line), line)
    return line

def copy_ticket_statuses(space1, space2):
    logger.debug('[TicketStatus] Starting')
    statuses1 = space1.get_ticket_statuses()
    statuses2 = space2.get_ticket_statuses()
    missing = []
    for status in statuses1:
        if status.name not in [s.name for s in statuses2]:
            missing.append(status)
        else:
            logger.debug('[TicketStatus] Skipping %s', status.name)
    for status in missing:
        logger.debug('[TicketStatus] Creating %s',status.name)
        s=space2.create_ticket_status(status)
    logger.debug('[TicketStatus] Finished')

def copy_ticket_custom_fields(space1, space2):
    logger.debug('[TicketCustomField] Starting')
    fields1 = space1.get_custom_fields()
    fields2 = space2.get_custom_fields()
    missing = []
    for field in fields1:
        if field.title not in [f.title for f in fields2]:
            missing.append(field)
        else:
            logger.debug('[TicketCustomField] Skipping %s',field.title)
    for field in missing:
        logger.debug('[TicketCustomField] Creating %s',field.title)
        space2.create_custom_field(field)
    logger.debug('[TicketCustomField] Finished')

def copy_ticket_components(space1, space2):
    logger.debug('[TicketComponent] Starting')
    components1 = space1.get_components()
    components2 = space2.get_components()
    existing_comp_map={}
    for c in components2:
        existing_comp_map[c.name] = c.id
    missing = []
    mapping = {}
    for component in components1:
        if component.name not in [c.name for c in components2]:
            missing.append(component)
        else:
            mapping[component.id] = existing_comp_map[component.name]
            logger.debug('[TicketComponent] Skipping %s',component.name)
    logger.debug('[TicketComponent] Found %s missing components: %s',
        len(missing), ', '.join([c.name for c in missing]))
    for component in missing:
        logger.debug('[TicketComponent] Creating %s',component.name)
        tc=space2.create_component(component)
        logger.debug('[TicketComponent] Created with id: %s', tc.id)
        mapping[component.id] = tc.id
    # Ticket component IDs may differ, return a mapping for new components
    logger.debug('[TicketComponent] Finished')
    return mapping

def copy_milestones(space1, space2):
    logger.debug('[Milestone] Starting')
    milestones1 = space1.get_milestones()
    milestones2 = space2.get_milestones()
    existing_ms_map={}
    for m in milestones2:
        existing_ms_map[m.title] = m.id
    missing = []
    mapping = {}
    for milestone in milestones1:
        if milestone.title not in [m.title for m in milestones2]:
            missing.append(milestone)
        else:
            mapping[milestone.id] = existing_ms_map[milestone.title]
            logger.debug('[Milestone] Skipping %s', milestone.title)
    for milestone in missing:
        logger.debug('[Milestone] Creating %s', milestone.title)
        ms=space2.create_milestone(milestone)
        mapping[milestone.id] = ms.id
    logger.debug('[Milestone] Finished')
    # Ticket milestone IDs may differ, return a mapping for new milestones
    return mapping

def copy_document(file_id, ticket1, ticket2, auth=None):
    logger.debug('[Document] Starting')
    if not auth:
        return
    from lxml.html import fromstring, submit_form
    import requests
    try:
        olddoc = ticket1.get_document(file_id)
    except AssemblaError:
        logger.debug('[Document] not found (probably deleted) %s', file_id)
        return
    login_url = 'https://www.assembla.com/do_login'
    client = requests.session()
    login_form = fromstring(client.get(login_url).content).forms[0]
    login_form.fields['user[login]'] = auth[0]
    login_form.fields['user[password]'] = auth[1]
    login_response = submit_form(login_form, open_http=client.request)
    if login_response.status_code != 200:
        raise AssemblaError('Failed on file download, status: %s' % login_response.status_code, response=login_response.content)
    response=client.get(olddoc.url)
    logger.debug('[Document] Attaching %s', olddoc.name)
    try:
        newdoc=ticket2.attach_file(response, olddoc)
    except AssemblaError, e:
        logger.debug('[Document] AssemblaError body:\n\n%s\n', e.response.content)
        logger.debug('[Document] Failed to attach source document: %s', olddoc.id)
    else:
        logger.debug('[Document] Attached with new id: %s', newdoc.id)
        logger.debug('[Document] Finished')

def copy_ticket_comments(ticket1, ticket2, number_map, auth=None):
    logger.debug('[TicketComment] Starting')
    comments = ticket1.get_comments()
    for c in comments:
        try:
            c_text = getattr(c, 'comment', None)
            if c.file:
                if auth: # allows for downloading files from www.assembla.com
                    copy_document(c.file, ticket1, ticket2, auth)
            else:
                if c_text and c.comment:
                    c.comment = remap_references(c.comment, number_map)
                    logger.debug('[TicketComment] Creating for %s', c.id)
                    cnew = ticket2.create_comment(c)
                    logger.debug('[TicketComment] Created %s', cnew.id)
                else:
                    # emulate system comment through regular comment
                    c.comment = prettify(c.ticket_changes)
                    logger.debug('[TicketComment] Emulating for %s', c.id)
                    cnew = ticket2.create_comment(c)
                    logger.debug('[TicketComment] Emulated %s', cnew.id)
        except AssemblaError, e:
            logger.debug('[TicketComment] Failed to process %s', c.id)
            logger.debug('[TicketComment] Response:\n\n%s\n', e.response.content)
            continue
    logger.debug('[TicketComment] Finished')