"""Attendee Registration Views

A registration is processed as follows:

1. start at the register view.
  1.1. if a user has a registration or invoices, warn about that
       (but only once, and force 'continue') [Hard to do! seperate view?]
  1.2. if a 'add reg' POST, then add another person to be registered to the
       form list.
  1.3. if 'remove reg' then remove that particular form from the form list.
  1.4. if 'proceed to checkout' then
    1.4.1. create Invoice and Registration objects.
    1.4.2. mark Invoice 'CO' or allow checkout.
    1.4.3. redirect to 'checkout' view.
2. in checkout view
  2.1. if Invoice is not marked 'CO' error appropriatly.
       if it is set to 'WA' then not that if there was a problem they should
       contact PyCon staff if something went wrong.
  2.2. mark Invoice 'WA' waiting for paypal ack.
  2.3. send e-mail to invoice person w/ the invoice and info on processing.
       they should get two further e-mails. One from verisign and one from
       us when we hear back from verisign. Note that if something goes wrong
       to please contact pycon staff. We can set the invoice back to 'CO'
       to try again. (We want to prevent double billing at all costs)
  2.4. Send e-mail messages to all non-invoice registrants.
       this should include 'connect' url to connect their django user to the
       registration in question. Include 'create account' link with redirect
       to connect for those who do not have an account. Include info that
       they can change parts of their registration if they wish.
  2.5. display invoice and verisign form of hidden inputs w/ submit button
       Submit goes to a verisign URL where the user inputs the data.
       NOTE: admin can later set invoice back to 'CO' to reprocess if user
             reports something went wrong and wants to try again.
3. in paypal_ack view (Only PayPal should use this view)
  3.1. if not a pOST, then error
  3.2. if no RESULT then error
  3.3. Create a new PayPalAck, record the IP and POST fields.
  3.4. if debug, then keep a record of all post data.
  3.5. if no INVOICE then assume it's a donation and return OK.
  3.4. Mark Invoice 'PA' paid via ack.
  3.5. Mark Registrations paid and active.
  3.6. Send e-mail to invoice owner mentioning that the payment was
       acknowledged and registrations are paid and active.
  3.7. Send e-mail to non-invoice owner registrations noting
       that the registrations are now paid and active.
       Include 'connect' links for those registrations still not connected
       to a user. not that they can change their registration if they so
       wish.
X. in thankyou (UPDATE, use a rstpage instead w/ link to invoice listing).

### other connected views (that's a pun)
4. in connect view (connect user to reg)
5. invoices (list all invoices and registrations for a user)
6. invoice (view invoice w/ links to edit, request change)
7. view_reg (view a registration w/ links to edit, request change)


### make changes (Only non-cost fileds are editable, request change otherwise)
8. in change_request
  8.1. if not a valid invoice or reg, or user not assoc, error
  8.2. Create a new Request, and send e-meil to list, reg head, requestor,
       and if a reg request (vs. invoice), if not the invoice owner, then
       send to the invoice owner as well.
       At this time all followup will be CC's to the list, and mnaged via
       admin interface. May require refund or additional purchase.

9. in edit
  9.1. If canceled, give message that edits are not allowed.
  9.2. Even if not paid or inactive, allow edits of non-pay fields.

"""
from django.template import loader, RequestContext
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.http import HttpResponseNotFound, HttpResponseNotAllowed
from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.core.cache import cache
from django.views.generic.list_detail import object_list
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from templatetags.attendeereg import can_view_invoice, can_view_reg
from pycon.features.models import Feature
from pycon.features.decorators import feature_required, login_required
from models import *
from forms import *


def create_invoice(user, total, donation, regforms):
    is_valid = reduce(lambda x, y: x and y.is_valid(),
                      regforms, donation.is_valid())
    if not is_valid: return None
    invoice = Invoice()
    invoice.user   = user
    invoice.status = 'CO' ## allow checkout
    invoice.donation = donation.cleaned_data['donation']
    invoice.total_cost = total
    invoice.save()
    registrations = [regform.save(invoice) for regform in regforms]
    invoice.regenerate = True
    invoice.save()
    ## send e-mails!!!
    return invoice

# Top Registration Form
@login_required
@feature_required("Register")
def register(request):
    """Initial registration view. We are going to cheat a bit here.
    We will allow for having multiple registrations on the same page.
    The user must click 'add' which will call this view again, and have
    anouther registration form added to the page. They can also click
    'remove' to remove a specific registration. This is all done via post.
    The add and remove 'links' are form post buttons (or should be).

    Then when they select the checkout button, that comes here too.
    That is where the Invoice and Registration objects are actually
    created. Then we return a redirect to the real checkout fort the invoice.
    One issue is what the %$^& you do if someone clicks checkout
    and then wants to update things. Once a person has gone to checkout,
    they have an invoice. They may never go to paypal, and it will sit in
    limbo going forward. Not much we can do about that.
    We might want to have a 'cancel' link on that page, but all it would do
    is mark the invoice 'canceled' or something. We still want it incase
    we DO get a paypal ack for it.

    We could hook in some javascript on the paypal post, or allow for going
    to checkout for an existing invoice to try to re-process. I would
    like to do that, but only if time permits. And it would break things for
    people w/o javascript (risky).

    """
    early  = Feature.objects.get(name='EarlyBirdReg').is_enabled
    count  = 1
    post   = None
    delreg = -1
    total  = Decimal("0.00")
    if request.method == 'POST':
        post = request.POST
        count = int(post['reg_sentinal_count'], 10)
        for ent in post:
            if ent.startswith('__delreg'):
                delreg = int(ent[8:], 10)
                break;

    donation = DonationForm(request.user, data=post)
    regs = [NewRegEntryForm(early,
                             user=request.user if i == 0 else None,
                             data=post,
                             prefix='reg%d'%i)
            for i in xrange(count)
                if (i != delreg and
                    (post is None or (('reg%d-sentinal'%i) in post)))]
    if donation.is_valid(): total=donation.cleaned_data['donation']
    for reg in regs: total+=reg.subtotal()
    if post is not None and delreg == -1:
        if '__addreg' in post:
            regs.append( NewRegEntryForm(early, prefix='reg%d'%count))
            count+=1
        elif '__checkout' in post:
            ## validate and save data if it passes
            invoice = create_invoice(request.user, total, donation, regs)
            if invoice is not None:
                ## redirect to checkout!
                return HttpResponseRedirect(
                    reverse('reg-checkout',None,(),{'invid':unicode(invoice)}))

    context = {}
    context['title'] = _("Registration")
    context['donation_form'] = donation
    context['reg_forms'] = regs
    context['reg_sentinal_count'] = count
    context['total'] = total

    return render_to_response('attendeereg/registration.html', context,
                              context_instance=RequestContext(request))

# Add Registration

# Checkout
@login_required
@feature_required("Register")
def checkout(request, invid):
    """Here we present the user with the invoice, and a checkout submit
    button. There is a form which is all hidden values. The POST goes
    to a verisign URL, where the user will pay by CCard.
    """
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    ## add check on the invoice status (can we do another checkout?)
    if invoice.status != u'CO':
        return render_to_response('attendeereg/no_checkout.html',
                              {'title': 'Checkout',
                               'invoice': invoice},
                              context_instance=RequestContext(request))
    auto_aubmit = request.method == 'POST' and '__checkout' in request.POST
    invoice.status='WA' ## waiting for paypal ack
    invoice.save()
    form=PayPalCheckoutForm(invoice)
    return render_to_response('attendeereg/checkout.html',
                              {'title': 'Checkout',
                               'form': form,
                               'auto_submit': auto_aubmit},
                              context_instance=RequestContext(request))

# Thankyou

# PayPal Silent POST
def paypal_ack(request):
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])
    feat = Feature.objects.get(name='HandlePayPalAck')
    if feat.is_disabled: return HttpResponseForbidden()
    #if 'RESULT' not in request.POST or 'PNREF' not in request.POST:
    #    return HttpResponseForbidden()
    ack = PayPalAck()
    ack.manual_reg = False
    ack.from_ip = request.META['REMOTE_ADDR']
    for fieldname in PayPalAck.post_fields:
        if fieldname in request.POST:
            val = request.POST[fieldname]
            if fieldname == 'RESULT':
                try:
                    val = int(val, 10)
                except:
                    val = -1
            setattr(ack, fieldname, val)
    ack.recieved_ack = True
    if settings.DEBUG:
        if not ack.RESULTMSG: ack.RESULTMSG = u''
        ack.RESULTMSG += repr(request.POST)
    ack.save() ## save immediatly!!
    if 'INVOICE' in request.POST and 'RESULT' in request.POST:
        ## looks like one of ours!
        try:
            invoice = Invoice.objects.get_invoice(request.POST['INVOICE'])
            ## RED_FLAG: add code to validate the sucker!
            ack.invoice = invoice
            ack.save()
            ## mark them paid and active
            if ack.RESULT == 0:
                if invoice.status == 'WA':
                    ## only update it if we were waiting for an acc
                    invoice.status='PA'
                    invoice.save()
                ## reguardless, mark the registrations paid.
                for reg in invoice.registrations.all():
                    reg.paid=True
                    reg.active=True
                    reg.save()
        except Invoice.DoesNotExist:
            pass

    return HttpResponse('Ok',status=200)

# Fake a PayPal Silent POST
@user_passes_test(lambda u: u.is_active and u.is_superuser)
@feature_required("FakePayPalAck")
def fake_paypal_ack(request):
    """We can not test the system without paypal being configured in test mode.
    This is not good for basic testing needs. With that in mind, this view
    will present superusers (and hopefully only superusers), the ability
    to fake a PayPal Silent POST for testing purposes.
    """
    form=FakePayPalAckForm()
    return render_to_response('attendeereg/fake_paypal_ack.html',
                              {'title': 'Fake PayPal Test Ack',
                               'form': form},
                              context_instance=RequestContext(request))

# View Reciept
@login_required
@feature_required("ViewRegInvoice")
def invoices(request):
    """List of a persons invoices and or registrations
    """
    invoices = request.user.invoice_set.all()
    registrations = request.user.registration_set.all()
    connections = Registration.objects.filter(user__isnull=True,
                                              email__iexact=request.user.email)
    return render_to_response('attendeereg/invoices.html',
                              {'title': 'Registration Invoices',
                               'invoices': invoices,
                               'registrations': registrations,
                               'connections': connections},
                              context_instance=RequestContext(request))


@login_required
@feature_required("ViewRegInvoice")
def invoice(request, invid):
    """View a specific invoice (with links to the reg's)
    """
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    if not can_view_invoice(request.user, invoice):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Invoice'},
                              context_instance=RequestContext(request))
    return render_to_response('attendeereg/invoice.html',
                              {'title': 'View Invoice',
                               'invoice': invoice},
                              context_instance=RequestContext(request))

# View Registration
@login_required
@feature_required("ViewRegInvoice")
def view_reg(request, regid):
    reg = get_object_or_404(Registration, pk=regid)
    if not can_view_reg(request.user, reg):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Registration'},
                              context_instance=RequestContext(request))
    return render_to_response('attendeereg/view.html',
                              {'title': 'View Registration',
                               'registration': reg},
                              context_instance=RequestContext(request))
# Edit Registration
@login_required
@feature_required("EditRegistration")
def edit(request, regid):
    reg = get_object_or_404(Registration, pk=regid)
    if not can_view_reg(request.user, reg):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Registration'},
                              context_instance=RequestContext(request))
    post = request.POST if request.method == 'POST' else None
    form = EditRegForm(reg, data=post)
    if form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse('reg-view',None,(),
                                            {'regid': str(reg.id)}))
    return render_to_response('attendeereg/edit.html',
                              {'title': 'Edit Registration',
                               'registration': reg,
                               'form': form},
                              context_instance=RequestContext(request))


# Connect Account To Registration
@login_required
@feature_required("ConnectRegistration")
def connect(request, regid, conid):
    reg = get_object_or_404(Registration, pk=regid)
    if conid.lower() != reg.connect_id.lower():
        return render_to_response('attendeereg/connect.html',
                                  {'title': 'Connect Registration',
                                   'registration': None,
                                   'connected': False,
                                   'invalid': True},
                                  context_instance=RequestContext(request))
    if reg.user is not None:
        if reg.user_id == request.user.id:
            return HttpResponseRedirect(
                reverse('reg-invoice-listing',None,(),{}))
        return render_to_response('attendeereg/connect.html',
                                  {'title': 'Connect Registration',
                                   'registration': None,
                                   'invalid': False,
                                   'connected': True},
                                  context_instance=RequestContext(request))
    if request.method == 'POST':
        if '__connect' in request.POST:
            reg.user = request.user
            reg.save()
            return HttpResponseRedirect(reverse('reg-view',
                                                None,(),{'regid':str(reg.id)}))
        return HttpResponseRedirect(reverse('reg-invoice-listing',None,(),{}))
    return render_to_response('attendeereg/connect.html',
                              {'title': 'Connect Registration',
                               'invalid': False,
                               'connected': False,
                               'registration': reg},
                              context_instance=RequestContext(request))

# Request Registration Change
@login_required
@feature_required("ChangeRegistrationRequest")
def change_request(request, regid=None, invid=None):
    pass
