# -*- coding: utf-8 -*-
"""
    tests.views
    ~~~~~~~~~~~

    Pluggable views.

    :copyright: © 2010 by the Pallets team.
    :license: BSD, see LICENSE for more details.
"""

import pytest

import flask
import flask.views

from werkzeug.http import parse_set_header


def common_test(app):
    c = app.test_client()

    assert c.get('/').data == b'GET'
    assert c.post('/').data == b'POST'
    assert c.put('/').status_code == 405
    meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
    assert sorted(meths) == ['GET', 'HEAD', 'OPTIONS', 'POST']


def test_basic_view(app):
    class Index(flask.views.View):
        methods = ['GET', 'POST']

        def dispatch_request(self):
            return flask.request.method

    app.add_url_rule('/', view_func=Index.as_view('index'))
    common_test(app)


def test_method_based_view(app):
    class Index(flask.views.MethodView):
        def get(self):
            return 'GET'

        def post(self):
            return 'POST'

    app.add_url_rule('/', view_func=Index.as_view('index'))

    common_test(app)


def test_view_patching(app):
    class Index(flask.views.MethodView):
        def get(self):
            1 // 0

        def post(self):
            1 // 0

    class Other(Index):
        def get(self):
            return 'GET'

        def post(self):
            return 'POST'

    view = Index.as_view('index')
    view.view_class = Other
    app.add_url_rule('/', view_func=view)
    common_test(app)


def test_view_inheritance(app, client):
    class Index(flask.views.MethodView):
        def get(self):
            return 'GET'

        def post(self):
            return 'POST'

    class BetterIndex(Index):
        def delete(self):
            return 'DELETE'

    app.add_url_rule('/', view_func=BetterIndex.as_view('index'))

    meths = parse_set_header(client.open('/', method='OPTIONS').headers['Allow'])
    assert sorted(meths) == ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST']


def test_view_decorators(app, client):
    def add_x_parachute(f):
        def new_function(*args, **kwargs):
            resp = flask.make_response(f(*args, **kwargs))
            resp.headers['X-Parachute'] = 'awesome'
            return resp

        return new_function

    class Index(flask.views.View):
        decorators = [add_x_parachute]

        def dispatch_request(self):
            return 'Awesome'

    app.add_url_rule('/', view_func=Index.as_view('index'))
    rv = client.get('/')
    assert rv.headers['X-Parachute'] == 'awesome'
    assert rv.data == b'Awesome'


def test_view_provide_automatic_options_attr():
    app = flask.Flask(__name__)

    class Index1(flask.views.View):
        provide_automatic_options = False

        def dispatch_request(self):
            return 'Hello World!'

    app.add_url_rule('/', view_func=Index1.as_view('index'))
    c = app.test_client()
    rv = c.open('/', method='OPTIONS')
    assert rv.status_code == 405

    app = flask.Flask(__name__)

    class Index2(flask.views.View):
        methods = ['OPTIONS']
        provide_automatic_options = True

        def dispatch_request(self):
            return 'Hello World!'

    app.add_url_rule('/', view_func=Index2.as_view('index'))
    c = app.test_client()
    rv = c.open('/', method='OPTIONS')
    assert sorted(rv.allow) == ['OPTIONS']

    app = flask.Flask(__name__)

    class Index3(flask.views.View):
        def dispatch_request(self):
            return 'Hello World!'

    app.add_url_rule('/', view_func=Index3.as_view('index'))
    c = app.test_client()
    rv = c.open('/', method='OPTIONS')
    assert 'OPTIONS' in rv.allow


def test_implicit_head(app, client):
    class Index(flask.views.MethodView):
        def get(self):
            return flask.Response('Blub', headers={
                'X-Method': flask.request.method
            })

    app.add_url_rule('/', view_func=Index.as_view('index'))
    rv = client.get('/')
    assert rv.data == b'Blub'
    assert rv.headers['X-Method'] == 'GET'
    rv = client.head('/')
    assert rv.data == b''
    assert rv.headers['X-Method'] == 'HEAD'


def test_explicit_head(app, client):
    class Index(flask.views.MethodView):
        def get(self):
            return 'GET'

        def head(self):
            return flask.Response('', headers={'X-Method': 'HEAD'})

    app.add_url_rule('/', view_func=Index.as_view('index'))
    rv = client.get('/')
    assert rv.data == b'GET'
    rv = client.head('/')
    assert rv.data == b''
    assert rv.headers['X-Method'] == 'HEAD'


def test_endpoint_override(app):
    app.debug = True

    class Index(flask.views.View):
        methods = ['GET', 'POST']

        def dispatch_request(self):
            return flask.request.method

    app.add_url_rule('/', view_func=Index.as_view('index'))

    with pytest.raises(AssertionError):
        app.add_url_rule('/', view_func=Index.as_view('index'))

    # But these tests should still pass. We just log a warning.
    common_test(app)


def test_multiple_inheritance(app, client):
    class GetView(flask.views.MethodView):
        def get(self):
            return 'GET'

    class DeleteView(flask.views.MethodView):
        def delete(self):
            return 'DELETE'

    class GetDeleteView(GetView, DeleteView):
        pass

    app.add_url_rule('/', view_func=GetDeleteView.as_view('index'))

    assert client.get('/').data == b'GET'
    assert client.delete('/').data == b'DELETE'
    assert sorted(GetDeleteView.methods) == ['DELETE', 'GET']


def test_remove_method_from_parent(app, client):
    class GetView(flask.views.MethodView):
        def get(self):
            return 'GET'

    class OtherView(flask.views.MethodView):
        def post(self):
            return 'POST'

    class View(GetView, OtherView):
        methods = ['GET']

    app.add_url_rule('/', view_func=View.as_view('index'))

    assert client.get('/').data == b'GET'
    assert client.post('/').status_code == 405
    assert sorted(View.methods) == ['GET']
