from hashlib import sha1

from werkzeug.http import is_resource_modified
from flask import Response, request

from grano.core import app


class NotModified(Exception):
    pass


@app.errorhandler(NotModified)
def handle_not_modified(exc):
    return Response(status=304)


def generate_etag(keys):
    args = sorted(set(request.args.items() + keys.items()))
    # jquery where is your god now?!?
    args = filter(lambda (k, v): k != '_', args)
    args = [k + ':' + repr(v) for k, v in args]
    return sha1('|'.join(args)).hexdigest()


def validate_cache(keys=None, last_modified=None):
    if isinstance(last_modified, (list, tuple)):
        last_modified = max(last_modified)
    if keys is None or not isinstance(keys, dict):
        keys = {'keys': keys}
    keys['last_modified'] = last_modified
    request._grano_etag = generate_etag(keys)
    request._grano_modified = last_modified
    if not is_resource_modified(request.environ,
                                etag=request._grano_etag,
                                last_modified=last_modified):
        raise NotModified()
    if request.if_none_match == request._grano_etag:
        raise NotModified()
    request.no_cache = False


def disable_cache():
    request.no_cache = True


@app.before_request
def setup_cache():
    request._grano_etag = None
    request._grano_modified = None
    request.no_cache = True


@app.after_request
def configure_cache_headers(response_class):
    if (not app.config.get('CACHE')) or request.no_cache:
        return response_class
    if request.method in ['GET', 'HEAD', 'OPTIONS'] \
            and response_class.status_code < 400 \
            and not response_class.is_streamed:

        response_class.cache_control.max_age = app.config.get('CACHE_AGE')
        response_class.cache_control.must_revalidate = True
        if request.account is not None:
            response_class.cache_control.private = True
        else:
            response_class.cache_control.public = True
        if request._grano_modified:
            response_class.last_modified = request._grano_modified
        if request._grano_etag:
            response_class.add_etag(request._grano_etag)
    return response_class


@app.after_request
def add_cors_header(response):
    if request.method in ('GET', 'HEAD', 'OPTIONS'):
        response.headers['Access-Control-Allow-Origin'] = '*'
    return response
