"""
django-helpdesk - A Django powered ticket tracker for small enterprise.

(c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.

forms.py - Definitions of newforms-based forms for creating and maintaining
           tickets.
"""

import mimetypes

from django import forms
from django.forms import extras
from django.conf import settings
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
try:
    from django.utils import timezone
except ImportError:
    from datetime import datetime as timezone

from helpdesk.lib import send_templated_mail, safe_template_context
from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC, CustomField, TicketCustomFieldValue, TicketDependency
from helpdesk.settings import HAS_TAG_SUPPORT
from helpdesk import settings as helpdesk_settings

class EditTicketForm(forms.ModelForm):
    class Meta:
        model = Ticket
        exclude = ('created', 'modified', 'status', 'on_hold', 'resolution', 'last_escalation', 'assigned_to')
    
    def __init__(self, *args, **kwargs):
        """
        Add any custom fields that are defined to the form
        """
        super(EditTicketForm, self).__init__(*args, **kwargs)

        for field in CustomField.objects.all():
            try:
                current_value = TicketCustomFieldValue.objects.get(ticket=self.instance, field=field)
                initial_value = current_value.value
            except TicketCustomFieldValue.DoesNotExist:
                initial_value = None
            instanceargs = {
                    'label': field.label,
                    'help_text': field.help_text,
                    'required': field.required,
                    'initial': initial_value,
                    }
            if field.data_type == 'varchar':
                fieldclass = forms.CharField
                instanceargs['max_length'] = field.max_length
            elif field.data_type == 'text':
                fieldclass = forms.CharField
                instanceargs['widget'] = forms.Textarea
                instanceargs['max_length'] = field.max_length
            elif field.data_type == 'integer':
                fieldclass = forms.IntegerField
            elif field.data_type == 'decimal':
                fieldclass = forms.DecimalField
                instanceargs['decimal_places'] = field.decimal_places
                instanceargs['max_digits'] = field.max_length
            elif field.data_type == 'list':
                fieldclass = forms.ChoiceField
                choices = field.choices_as_array
                if field.empty_selection_list:
                    choices.insert(0, ('', '---------'))
                instanceargs['choices'] = choices
            elif field.data_type == 'boolean':
                fieldclass = forms.BooleanField
            elif field.data_type == 'date':
                fieldclass = forms.DateField
            elif field.data_type == 'time':
                fieldclass = forms.TimeField
            elif field.data_type == 'datetime':
                fieldclass = forms.DateTimeField
            elif field.data_type == 'email':
                fieldclass = forms.EmailField
            elif field.data_type == 'url':
                fieldclass = forms.URLField
            elif field.data_type == 'ipaddress':
                fieldclass = forms.IPAddressField
            elif field.data_type == 'slug':
                fieldclass = forms.SlugField
            
            self.fields['custom_%s' % field.name] = fieldclass(**instanceargs)

    def save(self, *args, **kwargs):
        for field, value in self.cleaned_data.items():
            if field.startswith('custom_'):
                field_name = field.replace('custom_', '')
                customfield = CustomField.objects.get(name=field_name)
                try:
                    cfv = TicketCustomFieldValue.objects.get(ticket=self.instance, field=customfield)
                except TicketCustomFieldValue.DoesNotExist:
                    cfv = TicketCustomFieldValue(ticket=self.instance, field=customfield)
                cfv.value = value
                cfv.save()
        return super(EditTicketForm, self).save(*args, **kwargs)


class EditFollowUpForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        "Filter not openned tickets here."
        super(EditFollowUpForm, self).__init__(*args, **kwargs)
        self.fields["ticket"].queryset = Ticket.objects.filter(status__in=(Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS))
    class Meta:
        model = FollowUp
        exclude = ('date', 'user',)

class TicketForm(forms.Form):
    
    queue = forms.ChoiceField(
        label=_('Queue'),
        required=True,
        choices=()
        )

    title = forms.CharField(
        max_length=100,
        required=True,
        widget=forms.TextInput(attrs={'size':'60'}),
        label=_('Summary of the problem'),
        )

    submitter_email = forms.EmailField(
        required=False,
        label=_('Submitter E-Mail Address'),
        widget=forms.TextInput(attrs={'size':'60'}),
        help_text=_('This e-mail address will receive copies of all public '
            'updates to this ticket.'),
        )

    body = forms.CharField(
        widget=forms.Textarea(attrs={'cols': 47, 'rows': 15}),
        label=_('Description of Issue'),
        required=True,
        )

    assigned_to = forms.ChoiceField(
        choices=(),
        required=False,
        label=_('Case owner'),
        help_text=_('If you select an owner other than yourself, they\'ll be '
            'e-mailed details of this ticket immediately.'),
        )

    priority = forms.ChoiceField(
        choices=Ticket.PRIORITY_CHOICES,
        required=False,
        initial='3',
        label=_('Priority'),
        help_text=_('Please select a priority carefully. If unsure, leave it '
            'as \'3\'.'),
        )

    due_date = forms.DateTimeField(
        widget=extras.SelectDateWidget,
        required=False,
        label=_('Due on'),
        )

    def clean_due_date(self):
        data = self.cleaned_data['due_date']
        #TODO: add Google calendar update hook
        #if not hasattr(self, 'instance') or self.instance.due_date != new_data:
        #    print "you changed!"
        return data

    attachment = forms.FileField(
        required=False,
        label=_('Attach File'),
        help_text=_('You can attach a file such as a document or screenshot to this ticket.'),
        )

    if HAS_TAG_SUPPORT:
        tags = forms.CharField(
            max_length=255,
            required=False,
            widget=forms.TextInput(),
            label=_('Tags'),
            help_text=_('Words, separated by spaces, or phrases separated by commas. '
                    'These should communicate significant characteristics of this '
                    'ticket'),
            )

    def __init__(self, *args, **kwargs):
        """
        Add any custom fields that are defined to the form
        """
        super(TicketForm, self).__init__(*args, **kwargs)
        for field in CustomField.objects.all():
            instanceargs = {
                    'label': field.label,
                    'help_text': field.help_text,
                    'required': field.required,
                    }
            if field.data_type == 'varchar':
                fieldclass = forms.CharField
                instanceargs['max_length'] = field.max_length
            elif field.data_type == 'text':
                fieldclass = forms.CharField
                instanceargs['widget'] = forms.Textarea
                instanceargs['max_length'] = field.max_length
            elif field.data_type == 'integer':
                fieldclass = forms.IntegerField
            elif field.data_type == 'decimal':
                fieldclass = forms.DecimalField
                instanceargs['decimal_places'] = field.decimal_places
                instanceargs['max_digits'] = field.max_length
            elif field.data_type == 'list':
                fieldclass = forms.ChoiceField
                choices = field.choices_as_array
                if field.empty_selection_list:
                    choices.insert(0, ('', '---------'))
                instanceargs['choices'] = choices
            elif field.data_type == 'boolean':
                fieldclass = forms.BooleanField
            elif field.data_type == 'date':
                fieldclass = forms.DateField
                instanceargs['widget'] = extras.SelectDateWidget
            elif field.data_type == 'time':
                fieldclass = forms.TimeField
            elif field.data_type == 'datetime':
                fieldclass = forms.DateTimeField
            elif field.data_type == 'email':
                fieldclass = forms.EmailField
            elif field.data_type == 'url':
                fieldclass = forms.URLField
            elif field.data_type == 'ipaddress':
                fieldclass = forms.IPAddressField
            elif field.data_type == 'slug':
                fieldclass = forms.SlugField
            
            self.fields['custom_%s' % field.name] = fieldclass(**instanceargs)


    def save(self, user):
        """
        Writes and returns a Ticket() object
        """

        q = Queue.objects.get(id=int(self.cleaned_data['queue']))

        t = Ticket(
            title=self.cleaned_data['title'],
            submitter_email=self.cleaned_data['submitter_email'],
            created=timezone.now(),
            status=Ticket.OPEN_STATUS,
            queue=q,
            description=self.cleaned_data['body'],
            priority=self.cleaned_data['priority'],
            due_date=self.cleaned_data['due_date'],
        )

        if HAS_TAG_SUPPORT:
            t.tags = self.cleaned_data['tags']

        if self.cleaned_data['assigned_to']:
            try:
                u = User.objects.get(id=self.cleaned_data['assigned_to'])
                t.assigned_to = u
            except User.DoesNotExist:
                t.assigned_to = None
        
        t.save(send_email=False)
        
        for field, value in self.cleaned_data.items():
            if field.startswith('custom_'):
                field_name = field.replace('custom_', '')
                customfield = CustomField.objects.get(name=field_name)
                cfv = TicketCustomFieldValue(ticket=t,
                            field=customfield,
                            value=value)
                cfv.save()

        f = FollowUp(   
            ticket=t,
            title=_('Ticket Opened'),
            date=timezone.now(),
            public=True,
            comment=self.cleaned_data['body'],
            user=user,
        )
        if self.cleaned_data['assigned_to']:
            f.title = _('Ticket Opened & Assigned to %(name)s') % {
                'name': t.get_assigned_to
            }

        f.save()
        
        files = []
        if self.cleaned_data['attachment']:
            f = self.cleaned_data['attachment']
            filename = f.name.replace(' ', '_')
            a = Attachment(
                followup=f,
                filename=filename,
                mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
                size=f.size,
            )
            a.file.save(f.name, f, save=False)
            a.save()
            
            if f.size < getattr(settings, 'MAX_EMAIL_ATTACHMENT_SIZE', 512000):
                # Only files smaller than 512kb (or as defined in 
                # settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
                files.append(a.file.path)

        context = safe_template_context(t)
        context['comment'] = f.comment
        
        #TODO:relocate all email-sending logic to Ticket.save()
        
        messages_sent_to = []

        if t.submitter_email:
            send_templated_mail(
                'newticket_submitter',
                context,
                recipients=t.submitter_email,
                sender=q.from_address,
                fail_silently=True,
                files=files,
                )
            messages_sent_to.append(t.submitter_email)

        if t.assigned_to and t.assigned_to != user \
        and t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) \
        and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
            send_templated_mail(
                'assigned_owner',
                context,
                recipients=t.assigned_to.email,
                sender=q.from_address,
                fail_silently=True,
                files=files,
            )
            messages_sent_to.append(t.assigned_to.email)

        if q.new_ticket_cc and q.new_ticket_cc not in messages_sent_to:
            send_templated_mail(
                'newticket_cc',
                context,
                recipients=q.new_ticket_cc,
                sender=q.from_address,
                fail_silently=True,
                files=files,
            )
            messages_sent_to.append(q.new_ticket_cc)

        if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc and q.updated_ticket_cc not in messages_sent_to:
            send_templated_mail(
                'newticket_cc',
                context,
                recipients=q.updated_ticket_cc,
                sender=q.from_address,
                fail_silently=True,
                files=files,
            )

        return t


class PublicTicketForm(forms.Form):
    queue = forms.ChoiceField(
        label=_('Queue'),
        required=True,
        choices=()
        )

    title = forms.CharField(
        max_length=100,
        required=True,
        widget=forms.TextInput(),
        label=_('Summary of your query'),
        )

    submitter_email = forms.EmailField(
        required=True,
        label=_('Your E-Mail Address'),
        help_text=_('We will e-mail you when your ticket is updated.'),
        )

    body = forms.CharField(
        widget=forms.Textarea(),
        label=_('Description of your issue'),
        required=True,
        help_text=_('Please be as descriptive as possible, including any '
            'details we may need to address your query.'),
        )

    priority = forms.ChoiceField(
        choices=Ticket.PRIORITY_CHOICES,
        required=True,
        initial='3',
        label=_('Urgency'),
        help_text=_('Please select a priority carefully.'),
        )

    due_date = forms.DateTimeField(
        widget=extras.SelectDateWidget,
        required=False,
        label=_('Due on'),
        )

    attachment = forms.FileField(
        required=False,
        label=_('Attach File'),
        help_text=_('You can attach a file such as a document or screenshot to this ticket.'),
        )

    def __init__(self, *args, **kwargs):
        """
        Add any custom fields that are defined to the form
        """
        super(PublicTicketForm, self).__init__(*args, **kwargs)
        for field in CustomField.objects.filter(staff_only=False):
            instanceargs = {
                    'label': field.label,
                    'help_text': field.help_text,
                    'required': field.required,
                    }
            if field.data_type == 'varchar':
                fieldclass = forms.CharField
                instanceargs['max_length'] = field.max_length
            elif field.data_type == 'text':
                fieldclass = forms.CharField
                instanceargs['widget'] = forms.Textarea
                instanceargs['max_length'] = field.max_length
            elif field.data_type == 'integer':
                fieldclass = forms.IntegerField
            elif field.data_type == 'decimal':
                fieldclass = forms.DecimalField
                instanceargs['decimal_places'] = field.decimal_places
                instanceargs['max_digits'] = field.max_length
            elif field.data_type == 'list':
                fieldclass = forms.ChoiceField
                choices = field.choices_as_array
                if field.empty_selection_list:
                    choices.insert(0, ('', '---------'))
                instanceargs['choices'] = choices
            elif field.data_type == 'boolean':
                fieldclass = forms.BooleanField
            elif field.data_type == 'date':
                fieldclass = forms.DateField
            elif field.data_type == 'time':
                fieldclass = forms.TimeField
            elif field.data_type == 'datetime':
                fieldclass = forms.DateTimeField
            elif field.data_type == 'email':
                fieldclass = forms.EmailField
            elif field.data_type == 'url':
                fieldclass = forms.URLField
            elif field.data_type == 'ipaddress':
                fieldclass = forms.IPAddressField
            elif field.data_type == 'slug':
                fieldclass = forms.SlugField
            
            self.fields['custom_%s' % field.name] = fieldclass(**instanceargs)

    def save(self):
        """
        Writes and returns a Ticket() object
        """

        q = Queue.objects.get(id=int(self.cleaned_data['queue']))

        t = Ticket(
            title=self.cleaned_data['title'],
            submitter_email=self.cleaned_data['submitter_email'],
            created=timezone.now(),
            status=Ticket.OPEN_STATUS,
            queue=q,
            description=self.cleaned_data['body'],
            priority=self.cleaned_data['priority'],
            due_date=self.cleaned_data['due_date'],
        )

        t.save()

        for field, value in self.cleaned_data.items():
            if field.startswith('custom_'):
                field_name = field.replace('custom_', '')
                customfield = CustomField.objects.get(name=field_name)
                cfv = TicketCustomFieldValue(ticket=t, field=customfield, value=value)
                cfv.save()

        f = FollowUp(
            ticket=t,
            title=_('Ticket Opened Via Web'),
            date=timezone.now(),
            public=True,
            comment=self.cleaned_data['body'],
        )

        f.save()

        files = []
        if self.cleaned_data['attachment']:
            f = self.cleaned_data['attachment']
            filename = f.name.replace(' ', '_')
            a = Attachment(
                followup=f,
                filename=filename,
                mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
                size=f.size,
            )
            a.file.save(f.name, f, save=False)
            a.save()
            
            if f.size < getattr(settings, 'MAX_EMAIL_ATTACHMENT_SIZE', 512000):
                # Only files smaller than 512kb (or as defined in 
                # settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
                files.append(a.file.path)

        context = safe_template_context(t)

        messages_sent_to = []

        send_templated_mail(
            'newticket_submitter',
            context,
            recipients=t.submitter_email,
            sender=q.from_address,
            fail_silently=True,
            files=files,
            )
        messages_sent_to.append(t.submitter_email)

        if q.new_ticket_cc and q.new_ticket_cc not in messages_sent_to:
            send_templated_mail(
                'newticket_cc',
                context,
                recipients=q.new_ticket_cc,
                sender=q.from_address,
                fail_silently=True,
                files=files,
                )
            messages_sent_to.append(q.new_ticket_cc)

        if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc and q.updated_ticket_cc not in messages_sent_to:
            send_templated_mail(
                'newticket_cc',
                context,
                recipients=q.updated_ticket_cc,
                sender=q.from_address,
                fail_silently=True,
                files=files,
                )

        return t


class UserSettingsForm(forms.Form):
    login_view_ticketlist = forms.BooleanField(
        label=_('Show Ticket List on Login?'),
        help_text=_('Display the ticket list upon login? Otherwise, the dashboard is shown.'),
        required=False,
        )

    email_on_ticket_change = forms.BooleanField(
        label=_('E-mail me on ticket change?'),
        help_text=_('If you\'re the ticket owner and the ticket is changed via the web by '
            'somebody else, do you want to receive an e-mail?'),
        required=False,
        )

    email_on_ticket_assign = forms.BooleanField(
        label=_('E-mail me when assigned a ticket?'),
        help_text=_('If you are assigned a ticket via the web, do you want to receive an e-mail?'),
        required=False,
        )

    email_on_ticket_apichange = forms.BooleanField(
        label=_('E-mail me when a ticket is changed via the API?'),
        help_text=_('If a ticket is altered by the API, do you want to receive an e-mail?'),
        required=False,
        )

    tickets_per_page = forms.IntegerField(
        label=_('Number of tickets to show per page'),
        help_text=_('How many tickets do you want to see on the Ticket List page?'),
        required=False,
        min_value=1,
        max_value=1000,
        )

    use_email_as_submitter = forms.BooleanField(
        label=_('Use my e-mail address when submitting tickets?'),
        help_text=_('When you submit a ticket, do you want to automatically use your e-mail '
            'address as the submitter address? You can type a different e-mail address when '
            'entering the ticket if needed, this option only changes the default.'),
        required=False,
        )

class EmailIgnoreForm(forms.ModelForm):
    class Meta:
        model = IgnoreEmail
        exclude = []

class TicketCCForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(TicketCCForm, self).__init__(*args, **kwargs)
        if helpdesk_settings.HELPDESK_STAFF_ONLY_TICKET_CC:
            users = User.objects.filter(is_active=True, is_staff=True).order_by('username')
        else:
            users = User.objects.filter(is_active=True).order_by('username')
        self.fields['user'].queryset = users 
    class Meta:
        model = TicketCC
        exclude = ('ticket',)

class TicketDependencyForm(forms.ModelForm):
    class Meta:
        model = TicketDependency
        exclude = ('ticket',)
