import sys
import json
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.core.urlresolvers import get_mod_func
from django.utils.functional import Promise
from django.template import RequestContext
from django.shortcuts import render
from datetime import datetime, date
from django.conf import settings
from django import forms
from smarturls import surl

if sys.version_info < (3,):
    try:
        from django.utils.translation import force_unicode
    except ImportError:
        from django.utils.encoding import force_unicode
    from urllib import quote as urlquote
else:
    # In Python 3 force_unicode does not exist for Django 1.5
    force_unicode = lambda text: text
    basestring = str
    from urllib.parse import quote as urlquote



class JSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, Promise):
            return force_unicode(o)
        if isinstance(o, datetime):
            return o.strftime('%Y-%m-%dT%H:%M:%S')
        if isinstance(o, date):
            return o.strftime('%Y-%m-%d')
        else:
            return super(JSONEncoder, self).default(o)


class JSONResponse(HttpResponse):
    def __init__(self, data, pretty=False, content_type="application/json"):
        if pretty:
            HttpResponse.__init__(
                self, content=json.dumps(data, cls=JSONEncoder),
                content_type=content_type
            )
        else:
            HttpResponse.__init__(
                self,
                content=json.dumps(
                    data, cls=JSONEncoder, indent=4, sort_keys=True
                ),
                content_type=content_type
            )


def get_form_representation(form):
    d = {}
    for field in form.fields:
        value = form.fields[field]
        dd = {}
        if value.label:
            dd["label"] = value.label.title()
        dd["help_text"] = value.help_text
        dd["required"] = value.required
        if field in form.initial:
            dd["initial"] = form.initial[field]
        if value.initial:
            dd["initial"] = value.initial
        d[field] = dd
    return d


class RequestForm(forms.Form):
    def __init__(self, request, *args, **kw):
        super(RequestForm, self).__init__(*args, **kw)
        self.request = request

    def get_json(self, saved):
        if hasattr(self, "obj"):
            if hasattr(self.obj, "get_json"):
                return self.obj.get_json()
            return self.obj
        if hasattr(saved, "get_json"):
            return saved.get_json()
        return saved

    def initialize(self, field=None, value=None, **kw):
        if field:
            self.fields[field].initial = value
        for k, v in kw.items():
            self.fields[k].initial = v
        return self

    def initialize_with_object(self, obj, *fields, **kw):
        for field in fields:
            self.fields[field].initial = getattr(obj, field)
        for ffield, ofield in kw.items():
            self.fields[ffield].initial = getattr(obj, ofield)
        return self

    def update_object(self, obj, *args, **kw):
        d = self.cleaned_data.get
        for arg in args:
            setattr(obj, arg, d(arg))
        for k, v in kw.items():
            setattr(obj, k, d(v))
        return obj


class ResponseReady(Exception):
    def __init__(self, response, *args, **kw):
        self.response = response
        super(ResponseReady, self).__init__(*args, **kw)


def _form_handler(
    request, form_cls, require_login=False, block_get=False, ajax=False,
    next=None, template=None, login_url=None, pass_request=True,
    validate_only=False, **kwargs
):
    """
    Some ajax heavy apps require a lot of views that are merely a wrapper
    around the form. This generic view can be used for them.
    """
    RESULT_KEY = getattr(settings, "RESULT_KEY", "response")
    request.REQUEST = request.GET.copy()
    request.REQUEST.update(request.POST)
    if "next" in request.REQUEST:
        next = request.REQUEST["next"]
    is_ajax = request.is_ajax() or ajax or request.REQUEST.get("json") == "true"
    if isinstance(form_cls, basestring):
        # can take form_cls of the form: "project.app.forms.FormName"
        mod_name, form_name = get_mod_func(form_cls)
        form_cls = getattr(__import__(mod_name, {}, {}, ['']), form_name)
    validate_only = (
        validate_only or request.REQUEST.get("validate_only") == "true"
    )
    if login_url is None:
        login_url = getattr(settings, "LOGIN_URL", "/login/")
    if callable(require_login):
        require_login = require_login(request)
    elif require_login:
        require_login = not request.user.is_authenticated()
    if require_login:
        redirect_url = "%s?next=%s" % (
            login_url, urlquote(request.get_full_path())
        )  # FIXME
        if is_ajax:
            return JSONResponse({'success': False, 'redirect': redirect_url})
        return HttpResponseRedirect(redirect_url)
    if block_get and request.method != "POST":
        raise Http404("only post allowed")
    if next:
        assert template, "template required when next provided"

    def get_form(with_data=False):
        _form = form_cls(request) if pass_request else form_cls()
        _form.next = next
        if with_data:
            _form.data = request.REQUEST
            _form.files = request.FILES
            _form.is_bound = True
        if hasattr(_form, "init"):
            res = _form.init(**kwargs)
            if res:
                raise ResponseReady(res)
        return _form

    if is_ajax and request.method == "GET":
        return JSONResponse(get_form_representation(get_form()))
    if template and request.method == "GET":
        return render(request, template, {"form": get_form()})
    form = get_form(with_data=True)
    if form.is_valid():
        if validate_only:
            return JSONResponse({"valid": True, "errors": {}})
        r = form.save()
        if is_ajax:
            return JSONResponse(
                {
                    'success': True,
                    RESULT_KEY: (
                        form.get_json(r) if hasattr(form, "get_json") else r
                    )
                }
            )
        if isinstance(r, HttpResponse):
            return r
        if next:
            return HttpResponseRedirect(next)
        if template:
            return HttpResponseRedirect(r)
        return JSONResponse(
            {
                'success': True,
                RESULT_KEY: (
                    form.get_json(r) if hasattr(form, "get_json") else r
                )
            }
        )
    if validate_only:
        if "field" in request.REQUEST:
            errors = form.errors.get(request.REQUEST["field"], "")
            if errors:
                errors = "".join(errors)
        else:
            errors = form.errors
        return JSONResponse({"errors": errors, "valid": not errors})
    if is_ajax:
        return JSONResponse({'success': False, 'errors': form.errors})
    if template:
        return render(request, template, {"form": form})
    return JSONResponse({'success': False, 'errors': form.errors})


def form_handler(*args, **kw):
    try:
        return _form_handler(*args, **kw)
    except ResponseReady as e:
        return e.response


def fhurl(reg, form_cls, decorator=lambda x: x, **kw):
    name = kw.pop("name", None)
    kw["form_cls"] = form_cls
    return surl(reg, decorator(form_handler), kw, name=name)


def try_del(d, *args):
    for f in args:
        try:
            del d[f]
        except KeyError:
            pass
    return d
