# -*- coding: utf-8 -*-
# Ticket deleting plugins

from trac import __version__ as TRAC_VERSION
from trac import ticket
from trac.admin.api import IAdminPanelProvider
from trac.core import *
from trac.ticket.model import Ticket
from trac.web.api import IRequestFilter
from trac.web.chrome import ITemplateProvider, add_script, add_stylesheet
from trac.util import sorted
from trac.util.datefmt import to_datetime, utc, to_timestamp

import re
import traceback
import pprint
from time import strftime, localtime, mktime
from datetime import datetime

__all__ = ['TicketDeletePlugin']

class TicketDeletePlugin(Component):
    """A small ticket deletion plugin."""
    
    implements(ITemplateProvider, IAdminPanelProvider, IRequestFilter)

    # IRequestFilter methods
    def pre_process_request(self, req, handler):
        return handler

    def post_process_request(self, req, template, data, content_type):
        if template == 'ticket.html' and 'TICKET_ADMIN' in req.perm:
            add_script(req, 'ticketdelete/ticketdelete.js')
            add_stylesheet(req, 'ticketdelete/ticketdelete.css')
        return template, data, content_type
 
    # IAdminPanelProvider methods
    def get_admin_panels(self, req):
        if 'TICKET_ADMIN' in req.perm:
            yield ('ticket', 'チケットシステム', 'delete', 'チケットの削除')
            yield ('ticket', 'チケットシステム', 'comments', 'コメントの削除')
            
    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require('TICKET_ADMIN')
        
        data = {}

        data['href'] = req.href('admin', cat, page)
        data['page'] = page
        data['redir'] = 1
        data['changes'] = {}
        data['id'] = 0

        if req.method == 'POST':
            if page == 'delete':
                if 'ticketid' in req.args and 'ticketid2' in req.args:
                    if req.args.get('ticketid') == req.args.get('ticketid2'):
                        t = self._validate(req, req.args.get('ticketid'), data)
                        if t:
                            self._delete_ticket(t.id)
                            data['message'] = "Ticket #%s has been deleted." % t.id
                            
                    else:
                        data['message'] = "The two IDs did not match. Please try again."
            elif page == 'comments':
                if 'ticketid' in req.args:
                    req.redirect(req.href.admin(cat, page, req.args.get('ticketid')))
                else:
                    t = self._validate(req, path_info, data)
                    if t:
                        data['href'] = req.href('admin', cat, page, path_info)
                        
                        deletions = None
                        if "multidelete" in req.args:
                            deletions = [x.split('_') for x in req.args.getlist('mdelete')]
                            deletions.sort(lambda a,b: cmp(b[1],a[1]))
                        else:
                            buttons = [x[6:] for x in req.args.keys() if x.startswith('delete')]
                            deletions = [buttons[0].split('_')]
                            
                        if deletions:
                            for field, ts in deletions:
                                if ts != '':
                                    self.log.debug('TicketDelete: Deleting change to ticket %s at %s (%s)'%(t.id,ts,field))
                                    self._delete_change(t.id, ts, field)
                                    data['message'] = "Change to ticket #%s at %s has been modified" % (t.id, ts)
                                    data['redir'] = 0
                    
                
        elif path_info:
            t = self._validate(req, path_info, data)
            if t:
                if page == 'comments':
                    try:
                        selected = int(req.args.get('cnum')) - 1
                    except (TypeError, ValueError):
                        selected = None

                    ticket_data = {}
                    for time, author, field, oldvalue, newvalue, perm in t.get_changelog():
                        c_data = ticket_data.setdefault(to_timestamp(time), {})
                        c_data.setdefault('fields', {})[field] = {'old': oldvalue, 'new': newvalue}
                        c_data['author'] = author
                        # FIXME: The datetime handling is not working - enable
                        # for traceback
                        c_data['prettytime'] = strftime('%Y/%b/%d  %H:%M:%S',time.timetuple())
                    
                    # Check the boxes next to change number `selected`
                    time_list = list(sorted(ticket_data.iterkeys()))
                    if selected is not None and selected < len(time_list):
                        ticket_data[time_list[selected]]['checked'] = True
                    data['changes'] = ticket_data
                elif page == 'delete':
                    data['id'] = t.id
 
        return 'ticketdelete_admin.html', {'ticketdelete': data}

    # ITemplateProvider methods
    def get_templates_dirs(self):
        """
        Return the absolute path of the directory containing the provided
        ClearSilver templates.
        """
        from pkg_resources import resource_filename
        return [resource_filename(__name__, 'templates')]

    def get_htdocs_dirs(self):
        """
        Return a list of directories with static resources (such as style
        sheets, images, etc.)

        Each item in the list must be a `(prefix, abspath)` tuple. The
        `prefix` part defines the path in the URL that requests to these
        resources are prefixed with.
        
        The `abspath` is the absolute path to the directory containing the
        resources on the local file system.
        """
        from pkg_resources import resource_filename
        return [('ticketdelete', resource_filename(__name__, 'htdocs'))]

    # Internal methods
    def _get_trac_version(self):
        md = re.match('(\d+)\.(\d+)',TRAC_VERSION)
        if md:
            return (int(md.group(1)),int(md.group(2)))
        else:
            return (0,0)

    def _validate(self, req, arg, data):
        """Validate that arg is a string containing a valid ticket ID."""
        try:
            id = int(arg)
            t = Ticket(self.env, id)
            return t
        except TracError:
            data['message'] = "Ticket #%s not found. Please try again." % id
        except ValueError:
            data['message'] = "Ticket ID '%s' is not valid. Please try again." % arg
        return False
                                                                                                                
    
    def _delete_ticket(self, id):
        """Delete the given ticket ID."""
        major, minor = self._get_trac_version()
        if major > 0 or minor >= 10:
            ticket = Ticket(self.env,id)
            ticket.delete()
        else:
            db = self.env.get_db_cnx()
            cursor = db.cursor()
            cursor.execute("DELETE FROM ticket WHERE id=%s", (id,))
            cursor.execute("DELETE FROM ticket_change WHERE ticket=%s", (id,))
            cursor.execute("DELETE FROM attachment WHERE type='ticket' and id=%s", (id,))
            cursor.execute("DELETE FROM ticket_custom WHERE ticket=%s", (id,))
            db.commit()
            
    def _delete_change(self, id, ts, field=None):
        """Delete the change on the given ticket at the given timestamp."""
        db = self.env.get_db_cnx()
        cursor = db.cursor()
        ticket = Ticket(self.env,id)
        if field:
            if field == 'attachment':
                cursor.execute("DELETE FROM attachment WHERE type = 'ticket' AND id = %s AND time = %s", (id, ts))
            else:
                custom_fields = [f['name'] for f in ticket.fields if f.get('custom')]
                if field != "comment" and not [1 for time, author, field2, oldval, newval, _ in ticket.get_changelog() if to_timestamp(time) > int(ts) and field == field2]:
                    oldval = [old for _, _, field2, old, _, _ in ticket.get_changelog(to_datetime(int(ts))) if field2 == field][0]
                    if field in custom_fields:
                        cursor.execute("UPDATE ticket_custom SET value=%s WHERE ticket=%s AND name=%s", (oldval, id, field))
                    else:
                        cursor.execute("UPDATE ticket SET %s=%%s WHERE id=%%s" % field, (oldval, id))
                cursor.execute("DELETE FROM ticket_change WHERE ticket = %s AND time = %s AND field = %s", (id, ts, field))
        else:
            for _, _, field, _, _, _ in ticket.get_changelog(to_datetime(int(ts), utc)):
                self._delete_change(id, ts, field)
            
        db.commit()
