#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program.  If not, see
# <https://www.gnu.org/licenses/>.
#
import datetime
import json
from decimal import Decimal

from bs4 import BeautifulSoup
from django.conf import settings
from django.test import TestCase, override_settings
from django.utils.timezone import now
from django_scopes import scopes_disabled
from freezegun import freeze_time

from pretix.base.models import Order, OrderPosition
from pretix.presale.style import regenerate_css, regenerate_organizer_css

from .test_cart import CartTestMixin


class WidgetCartTest(CartTestMixin, TestCase):
    @scopes_disabled()
    def setUp(self):
        super().setUp()
        self.order = Order.objects.create(
            status=Order.STATUS_PENDING,
            event=self.event,
            email='admin@localhost',
            datetime=now() - datetime.timedelta(days=3),
            expires=now() + datetime.timedelta(days=11),
            total=Decimal("23"),
            locale='en'
        )
        self.ticket_pos = OrderPosition.objects.create(
            order=self.order,
            item=self.ticket,
            variation=None,
            price=Decimal("23"),
            attendee_name_parts={'full_name': "Peter"}
        )

    def test_iframe_entry_view_wrapper(self):
        self.event.settings.locales = ["de", "en"]
        r = self.client.get('/%s/%s/?iframe=1&locale=de' % (self.orga.slug, self.event.slug), headers={
            "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
        })
        assert 'iframe_session' in self.client.session
        assert self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value == "de"
        assert r.headers["Content-Language"] == "de"

    def test_allow_frame_if_namespaced(self):
        response = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
        assert 'X-Frame-Options' in response
        response = self.client.get('/%s/%s/w/aaaaaaaaaaaaaaaa/' % (self.orga.slug, self.event.slug))
        assert 'X-Frame-Options' not in response

        response = self.client.get('/%s/%s/waitinglist' % (self.orga.slug, self.event.slug))
        assert 'X-Frame-Options' in response
        response = self.client.get('/%s/%s/w/aaaaaaaaaaaaaaaa/waitinglist' % (self.orga.slug, self.event.slug))
        assert 'X-Frame-Options' not in response

    def test_allow_frame_on_order(self):
        response = self.client.get('/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
                                                            self.order.secret))
        assert 'X-Frame-Options' not in response
        response = self.client.get('/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
                                                                  self.order.secret))
        assert 'X-Frame-Options' not in response
        response = self.client.get('/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code,
                                                                      self.order.secret))
        assert 'X-Frame-Options' not in response
        response = self.client.get('/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code,
                                                                  self.order.secret))
        assert 'X-Frame-Options' not in response

    def test_allow_cors_if_namespaced(self):
        response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
            'item_%d' % self.ticket.id: '1',
            'ajax': 1
        })
        assert 'Access-Control-Allow-Origin' not in response
        response = self.client.post('/%s/%s/w/aaaaaaaaaaaaaaaa/cart/add' % (self.orga.slug, self.event.slug), {
            'item_%d' % self.ticket.id: '1',
            'ajax': 1
        })
        assert response['Access-Control-Allow-Origin'] == '*'

    def test_cart_isolation(self):
        response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
            'item_%d' % self.ticket.id: '1'
        }, follow=True)
        self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug),
                             target_status_code=200)
        doc = BeautifulSoup(response.rendered_content, "lxml")
        assert len(doc.select('.cart .cart-row')) == 2
        self.assertIn('Early-bird', doc.select('.cart .cart-row')[0].select('strong')[0].text)
        self.assertIn('1', doc.select('.cart .cart-row')[0].select('.count')[0].text)
        self.assertIn('23', doc.select('.cart .cart-row')[0].select('.price')[0].text)
        self.assertIn('23', doc.select('.cart .cart-row')[0].select('.price')[1].text)

        response = self.client.get('/%s/%s/w/aaaaaaaaaaaaaaaa/' % (self.orga.slug, self.event.slug))
        doc = BeautifulSoup(response.rendered_content, "lxml")
        assert len(doc.select('.cart .cart-row')) == 0
        response = self.client.post('/%s/%s/w/aaaaaaaaaaaaaaaa/cart/add' % (self.orga.slug, self.event.slug), {
            'item_%d' % self.ticket.id: '1'
        }, follow=True)
        self.assertRedirects(response, '/%s/%s/w/aaaaaaaaaaaaaaaa/?require_cookie=true' % (self.orga.slug, self.event.slug),
                             target_status_code=200)
        doc = BeautifulSoup(response.rendered_content, "lxml")
        assert len(doc.select('.cart .cart-row')) == 2
        self.assertIn('Early-bird', doc.select('.cart .cart-row')[0].select('strong')[0].text)
        self.assertIn('1', doc.select('.cart .cart-row')[0].select('.count')[0].text)
        self.assertIn('23', doc.select('.cart .cart-row')[0].select('.price')[0].text)
        self.assertIn('23', doc.select('.cart .cart-row')[0].select('.price')[1].text)

        response = self.client.get('/%s/%s/w/aaaaaaaaaaaaaaab/' % (self.orga.slug, self.event.slug))
        doc = BeautifulSoup(response.rendered_content, "lxml")
        assert len(doc.select('.cart .cart-row')) == 0
        response = self.client.post('/%s/%s/w/aaaaaaaaaaaaaaab/cart/add' % (self.orga.slug, self.event.slug), {
            'item_%d' % self.ticket.id: '1'
        }, follow=True)
        self.assertRedirects(response, '/%s/%s/w/aaaaaaaaaaaaaaab/?require_cookie=true' % (self.orga.slug, self.event.slug),
                             target_status_code=200)
        doc = BeautifulSoup(response.rendered_content, "lxml")
        assert len(doc.select('.cart .cart-row')) == 2
        self.assertIn('Early-bird', doc.select('.cart .cart-row')[0].select('strong')[0].text)
        self.assertIn('1', doc.select('.cart .cart-row')[0].select('.count')[0].text)
        self.assertIn('23', doc.select('.cart .cart-row')[0].select('.price')[0].text)
        self.assertIn('23', doc.select('.cart .cart-row')[0].select('.price')[1].text)

    def test_saleschannel_disabled(self):
        self.event.sales_channels = []
        self.event.save()
        response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
        data = json.loads(response.content.decode())
        assert data == {
            "error": "Tickets for this event cannot be purchased on this sales channel.",
            'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
        }
        self.assertEqual(response.status_code, 200)
        response = self.client.post('/%s/%s/w/aaaaaaaaaaaaaaab/cart/add' % (self.orga.slug, self.event.slug), {
            'item_%d' % self.ticket.id: '1'
        }, follow=True)
        self.assertEqual(response.status_code, 404)

    def test_product_list_view(self):
        response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data == {
            "target_url": "http://example.com/ccc/30c3/",
            "subevent": None,
            "name": "30C3",
            "date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
            "frontpage_text": "",
            "location": "",
            "currency": "EUR",
            "show_variations_expanded": False,
            "display_net_prices": False,
            "use_native_spinners": False,
            "vouchers_exist": False,
            "waiting_list_enabled": False,
            "error": None,
            "has_seating_plan": False,
            "has_seating_plan_waitinglist": False,
            'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
            "items_by_category": [
                {
                    "items": [
                        {
                            "current_unavailability_reason": None,
                            "order_min": None,
                            "max_price": None,
                            "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
                            "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
                            "picture": None,
                            "picture_fullsize": None,
                            "has_variations": 0,
                            "allow_waitinglist": True,
                            "mandatory_priced_addons": False,
                            "description": None,
                            "min_price": None,
                            "avail": [100, None],
                            "variations": [],
                            "id": self.ticket.pk,
                            "free_price": False,
                            "original_price": None,
                            "name": "Early-bird ticket",
                            "order_max": 4
                        },
                        {
                            "current_unavailability_reason": None,
                            "order_min": None,
                            "max_price": "14.00",
                            "price": None,
                            "suggested_price": None,
                            "picture": None,
                            "picture_fullsize": None,
                            "has_variations": 4,
                            "allow_waitinglist": True,
                            "mandatory_priced_addons": False,
                            "description": None,
                            "min_price": "12.00",
                            "avail": None,
                            "variations": [
                                {
                                    "value": "Red",
                                    "id": self.shirt_red.pk,
                                    'original_price': None,
                                    "price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
                                              "rate": "19.00", "includes_mixed_tax_rate": False},
                                    "suggested_price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
                                                        "rate": "19.00", "includes_mixed_tax_rate": False},
                                    "description": None,
                                    "avail": [100, None],
                                    "order_max": 2,
                                    "current_unavailability_reason": None,
                                },
                                {
                                    "value": "Blue",
                                    "id": self.shirt_blue.pk,
                                    'original_price': None,
                                    "price": {"gross": "12.00", "net": "10.08", "tax": "1.92", "name": "",
                                              "rate": "19.00", "includes_mixed_tax_rate": False},
                                    "suggested_price": {"gross": "12.00", "net": "10.08", "tax": "1.92", "name": "",
                                                        "rate": "19.00", "includes_mixed_tax_rate": False},
                                    "description": None,
                                    "avail": [100, None],
                                    "order_max": 2,
                                    "current_unavailability_reason": None,
                                }
                            ],
                            "id": self.shirt.pk,
                            "free_price": False,
                            "original_price": None,
                            "name": "T-Shirt",
                            "order_max": None
                        }
                    ],
                    "description": None,
                    "id": self.category.pk,
                    "name": "Everything"
                }
            ],
            "itemnum": 2,
            "display_add_to_cart": True,
            "cart_exists": False,
            "voucher_explanation_text": "",
        }

    def test_product_list_view_filter(self):
        response = self.client.get('/%s/%s/widget/product_list?items=%s' % (self.orga.slug, self.event.slug,
                                                                            self.ticket.pk))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data['items_by_category'] == [
            {
                "items": [
                    {
                        "current_unavailability_reason": None,
                        "order_min": None,
                        "max_price": None,
                        "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
                                  "includes_mixed_tax_rate": False},
                        "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
                                            "includes_mixed_tax_rate": False},
                        "picture": None,
                        "picture_fullsize": None,
                        "has_variations": 0,
                        "allow_waitinglist": True,
                        "mandatory_priced_addons": False,
                        "description": None,
                        "min_price": None,
                        "avail": [100, None],
                        "variations": [],
                        "id": self.ticket.pk,
                        "free_price": False,
                        "original_price": None,
                        "name": "Early-bird ticket",
                        "order_max": 4
                    }
                ],
                "description": None,
                "id": self.category.pk,
                "name": "Everything"
            }
        ]

        response = self.client.get('/%s/%s/widget/product_list?categories=0,%s' % (self.orga.slug, self.event.slug,
                                                                                   self.category.pk))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert len(data['items_by_category']) == 1

        response = self.client.get('/%s/%s/widget/product_list?categories=0' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert len(data['items_by_category']) == 0

    def test_product_list_view_variation_filter(self):
        response = self.client.get('/%s/%s/widget/product_list?variations=%s' % (self.orga.slug, self.event.slug,
                                                                                 self.shirt_red.pk))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data['items_by_category'] == [
            {
                "items": [
                    {
                        "current_unavailability_reason": None,
                        "order_min": None,
                        "max_price": "14.00",
                        "price": None,
                        "suggested_price": None,
                        "picture": None,
                        "picture_fullsize": None,
                        "has_variations": 4,
                        "allow_waitinglist": True,
                        "mandatory_priced_addons": False,
                        "description": None,
                        "min_price": "12.00",
                        "avail": None,
                        "variations": [
                            {
                                "value": "Red",
                                "id": self.shirt_red.pk,
                                'original_price': None,
                                "price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
                                          "rate": "19.00", "includes_mixed_tax_rate": False},
                                "suggested_price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
                                                    "rate": "19.00", "includes_mixed_tax_rate": False},
                                "description": None,
                                "avail": [100, None],
                                "order_max": 2,
                                "current_unavailability_reason": None,
                            }
                        ],
                        "id": self.shirt.pk,
                        "free_price": False,
                        "original_price": None,
                        "name": "T-Shirt",
                        "order_max": None
                    }
                ],
                "description": None,
                "id": self.category.pk,
                "name": "Everything"
            }
        ]

    def test_product_list_view_with_voucher(self):
        with scopes_disabled():
            self.event.vouchers.create(item=self.ticket, code="ABCDE")
        response = self.client.get('/%s/%s/widget/product_list?voucher=ABCDE' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data == {
            "target_url": "http://example.com/ccc/30c3/",
            "subevent": None,
            "name": "30C3",
            "date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
            "frontpage_text": "",
            "location": "",
            "currency": "EUR",
            "show_variations_expanded": False,
            "display_net_prices": False,
            "use_native_spinners": False,
            "has_seating_plan": False,
            "has_seating_plan_waitinglist": False,
            "vouchers_exist": True,
            "waiting_list_enabled": False,
            "error": None,
            'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
            "items_by_category": [
                {
                    "items": [
                        {
                            "current_unavailability_reason": None,
                            "order_min": None,
                            "max_price": None,
                            "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
                            "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
                            "picture": None,
                            "picture_fullsize": None,
                            "has_variations": 0,
                            "allow_waitinglist": True,
                            "mandatory_priced_addons": False,
                            "description": None,
                            "min_price": None,
                            "avail": [100, None],
                            "variations": [],
                            "id": self.ticket.pk,
                            "free_price": False,
                            "original_price": None,
                            "name": "Early-bird ticket",
                            "order_max": 4
                        },
                    ],
                    "description": None,
                    "id": self.category.pk,
                    "name": "Everything"
                }
            ],
            "itemnum": 1,
            "voucher_explanation_text": "",
            "display_add_to_cart": True,
            "cart_exists": False
        }

    def test_product_list_view_with_voucher_variation_through_quota(self):
        with scopes_disabled():
            self.event.vouchers.create(quota=self.quota_shirts, code="ABCDE")
            self.quota_shirts.variations.remove(self.shirt_blue)
        response = self.client.get('/%s/%s/widget/product_list?voucher=ABCDE' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data == {
            "target_url": "http://example.com/ccc/30c3/",
            "subevent": None,
            "name": "30C3",
            "date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
            "frontpage_text": "",
            "location": "",
            "currency": "EUR",
            "show_variations_expanded": False,
            "display_net_prices": False,
            "use_native_spinners": False,
            "vouchers_exist": True,
            "has_seating_plan": False,
            "has_seating_plan_waitinglist": False,
            "waiting_list_enabled": False,
            "error": None,
            'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
            "items_by_category": [
                {
                    "items": [
                        {
                            'id': self.shirt.pk,
                            'name': 'T-Shirt',
                            'picture': None,
                            "picture_fullsize": None,
                            'description': None,
                            'has_variations': 2,
                            "allow_waitinglist": True,
                            "mandatory_priced_addons": False,
                            'current_unavailability_reason': None,
                            'order_min': None,
                            'order_max': None,
                            'price': None,
                            'suggested_price': None,
                            'min_price': '14.00',
                            'max_price': '14.00',
                            'free_price': False,
                            'avail': None,
                            'original_price': None,
                            'variations': [
                                {
                                    'id': self.shirt_red.pk,
                                    'value': 'Red',
                                    'order_max': 2,
                                    'description': None,
                                    'original_price': None,
                                    'price': {
                                        'gross': '14.00',
                                        'net': '11.76',
                                        'tax': '2.24',
                                        'rate': '19.00',
                                        'name': '',
                                        'includes_mixed_tax_rate': False
                                    },
                                    'suggested_price': {
                                        'gross': '14.00',
                                        'net': '11.76',
                                        'tax': '2.24',
                                        'rate': '19.00',
                                        'name': '',
                                        'includes_mixed_tax_rate': False
                                    },
                                    'avail': [100, None],
                                    'current_unavailability_reason': None,
                                },
                            ]
                        }
                    ],
                    "description": None,
                    "id": self.category.pk,
                    "name": "Everything"
                }
            ],
            "itemnum": 1,
            "display_add_to_cart": True,
            "voucher_explanation_text": "",
            "cart_exists": False
        }

    def test_product_list_view_with_voucher_expired(self):
        with scopes_disabled():
            self.event.vouchers.create(item=self.ticket, code="ABCDE", valid_until=now() - datetime.timedelta(days=1))
        response = self.client.get('/%s/%s/widget/product_list?voucher=ABCDE' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data == {
            "target_url": "http://example.com/ccc/30c3/",
            "subevent": None,
            "name": "30C3",
            "date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
            "frontpage_text": "",
            "location": "",
            "currency": "EUR",
            'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
            "show_variations_expanded": False,
            "display_net_prices": False,
            "use_native_spinners": False,
            "has_seating_plan": False,
            "has_seating_plan_waitinglist": False,
            "vouchers_exist": False,
            "waiting_list_enabled": False,
            "error": "This voucher is expired.",
            "items_by_category": [],
            "display_add_to_cart": False,
            "cart_exists": False,
            "voucher_explanation_text": "",
            "itemnum": 0,
        }

    @override_settings(COMPRESS_PRECOMPILERS=settings.COMPRESS_PRECOMPILERS_ORIGINAL)
    def test_css_customized(self):
        response = self.client.get('/%s/%s/widget/v1.css' % (self.orga.slug, self.event.slug))
        c = b"".join(response.streaming_content).decode()
        assert '#7f5a91' in c
        assert '#33c33c' not in c
        assert '#34c34c' not in c

        self.orga.settings.primary_color = "#33c33c"
        regenerate_organizer_css.apply(args=(self.orga.pk,))
        response = self.client.get('/%s/%s/widget/v1.css' % (self.orga.slug, self.event.slug))
        c = b"".join(response.streaming_content).decode()
        assert '#7f5a91' not in c
        assert '#33c33c' in c
        assert '#34c34c' not in c

        self.event.settings.primary_color = "#34c34c"
        regenerate_css.apply(args=(self.event.pk,))
        response = self.client.get('/%s/%s/widget/v1.css' % (self.orga.slug, self.event.slug))
        c = b"".join(response.streaming_content).decode()
        assert '#7f5a91' not in c
        assert '#33c33c' not in c
        assert '#34c34c' in c

    def test_js_localized(self):
        response = self.client.get('/widget/v1.en.js')
        c = response.content.decode()
        assert '%m/%d/%Y' in c
        assert '%d.%m.%Y' not in c
        response = self.client.get('/widget/v1.de.js')
        c = response.content.decode()
        assert '%m/%d/%Y' not in c
        assert '%d.%m.%Y' in c

    def test_product_list_view_with_bundle_sold_out(self):
        self.quota_shirts.size = 0
        self.quota_shirts.save()
        self.ticket.bundles.create(bundled_item=self.shirt, bundled_variation=self.shirt_blue,
                                   designated_price=2, count=1)
        response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert data["items_by_category"][0]["items"][0]["avail"] == [0, None]

    def test_product_list_view_with_bundle_mixed_tax_rate(self):
        with scopes_disabled():
            self.tr7 = self.event.tax_rules.create(rate=Decimal('7.00'))
            self.shirt.tax_rule = self.tr7
            self.shirt.require_bundling = True
            self.shirt.save()
            self.ticket.bundles.create(bundled_item=self.shirt, bundled_variation=self.shirt_blue,
                                       designated_price=2, count=1)
        response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
        assert response['Access-Control-Allow-Origin'] == '*'
        data = json.loads(response.content.decode())
        assert len(data["items_by_category"][0]["items"]) == 1
        assert data["items_by_category"][0]["items"][0]["price"] == {
            "gross": "23.00",
            "net": "19.52",
            "tax": "3.48",
            "name": "MIXED!",
            "rate": "19.00",
            "includes_mixed_tax_rate": True
        }

    def test_subevent_list(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                self.event.subevents.create(name="Past", active=True, date_from=now() - datetime.timedelta(days=3))
                se1 = self.event.subevents.create(name="Present", active=True, date_from=now())
                se2 = self.event.subevents.create(name="Future", active=True, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Disabled", active=False, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))

            response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
            data = json.loads(response.content.decode())
            assert data == {
                'list_type': 'list',
                'meta_filter_fields': [],
                'name': '30C3',
                'frontpage_text': '',
                'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
                'has_more_events': False,
                'events': [
                    {'name': 'Present', 'date_range': 'Jan. 1, 2019 11:00', 'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                     'event_url': 'http://example.com/ccc/30c3/', 'subevent': se1.pk, 'location': ''},
                    {'name': 'Future', 'date_range': 'Jan. 4, 2019 11:00', 'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                     'event_url': 'http://example.com/ccc/30c3/', 'subevent': se2.pk, 'location': ''}
                ]
            }

    def test_subevent_calendar(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                self.event.subevents.create(name="Past", active=True, date_from=now() - datetime.timedelta(days=3))
                se1 = self.event.subevents.create(name="Present", active=True, date_from=now())
                se2 = self.event.subevents.create(name="Future", active=True, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Disabled", active=False, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))

            response = self.client.get('/%s/%s/widget/product_list?style=calendar' % (self.orga.slug, self.event.slug))
            data = json.loads(response.content.decode())
            assert data == {
                'list_type': 'calendar',
                'meta_filter_fields': [],
                'date': '2019-01-01',
                'name': '30C3',
                'frontpage_text': '',
                'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
                'weeks': [
                    [
                        None,
                        {'day': 1, 'date': '2019-01-01', 'events': [
                            {'name': 'Present', 'time': '11:00', 'continued': False, 'date_range': 'Jan. 1, 2019 11:00',
                             'location': '',
                             'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                             'event_url': 'http://example.com/ccc/30c3/', 'subevent': se1.pk}]},
                        {'day': 2, 'date': '2019-01-02', 'events': []},
                        {'day': 3, 'date': '2019-01-03', 'events': []},
                        {'day': 4, 'date': '2019-01-04', 'events': [
                            {'name': 'Future', 'time': '11:00', 'continued': False, 'date_range': 'Jan. 4, 2019 11:00',
                             'location': '',
                             'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                             'event_url': 'http://example.com/ccc/30c3/', 'subevent': se2.pk}]},
                        {'day': 5, 'date': '2019-01-05', 'events': []},
                        {'day': 6, 'date': '2019-01-06', 'events': []}
                    ],
                    [
                        {'day': 7, 'date': '2019-01-07', 'events': []},
                        {'day': 8, 'date': '2019-01-08', 'events': []},
                        {'day': 9, 'date': '2019-01-09', 'events': []},
                        {'day': 10, 'date': '2019-01-10', 'events': []},
                        {'day': 11, 'date': '2019-01-11', 'events': []},
                        {'day': 12, 'date': '2019-01-12', 'events': []},
                        {'day': 13, 'date': '2019-01-13', 'events': []}
                    ],
                    [
                        {'day': 14, 'date': '2019-01-14', 'events': []},
                        {'day': 15, 'date': '2019-01-15', 'events': []},
                        {'day': 16, 'date': '2019-01-16', 'events': []},
                        {'day': 17, 'date': '2019-01-17', 'events': []},
                        {'day': 18, 'date': '2019-01-18', 'events': []},
                        {'day': 19, 'date': '2019-01-19', 'events': []},
                        {'day': 20, 'date': '2019-01-20', 'events': []}
                    ],
                    [
                        {'day': 21, 'date': '2019-01-21', 'events': []},
                        {'day': 22, 'date': '2019-01-22', 'events': []},
                        {'day': 23, 'date': '2019-01-23', 'events': []},
                        {'day': 24, 'date': '2019-01-24', 'events': []},
                        {'day': 25, 'date': '2019-01-25', 'events': []},
                        {'day': 26, 'date': '2019-01-26', 'events': []},
                        {'day': 27, 'date': '2019-01-27', 'events': []}
                    ],
                    [
                        {'day': 28, 'date': '2019-01-28', 'events': []},
                        {'day': 29, 'date': '2019-01-29', 'events': []},
                        {'day': 30, 'date': '2019-01-30', 'events': []},
                        {'day': 31, 'date': '2019-01-31', 'events': []},
                        None, None, None
                    ]
                ]
            }

    def test_subevent_week_calendar(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                self.event.subevents.create(name="Past", active=True, date_from=now() - datetime.timedelta(days=3))
                se1 = self.event.subevents.create(name="Present", active=True, date_from=now())
                se2 = self.event.subevents.create(name="Future", active=True, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Disabled", active=False, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))

            response = self.client.get('/%s/%s/widget/product_list?style=week' % (self.orga.slug, self.event.slug))
            data = json.loads(response.content.decode())
            assert data == {
                'list_type': 'week',
                'meta_filter_fields': [],
                'name': '30C3',
                'frontpage_text': '',
                'week': [2019, 1],
                'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
                'days': [
                    {'day_formatted': 'Mon, Dec 31st', 'date': '2018-12-31', 'events': [], 'today': False},
                    {'day_formatted': 'Tue, Jan 1st', 'date': '2019-01-01', 'events': [
                        {'name': 'Present', 'time': '11:00', 'continued': False, 'date_range': 'Jan. 1, 2019 11:00',
                         'location': '',
                         'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                         'event_url': 'http://example.com/ccc/30c3/', 'subevent': se1.pk}], 'today': True},
                    {'day_formatted': 'Wed, Jan 2nd', 'date': '2019-01-02', 'events': [], 'today': False},
                    {'day_formatted': 'Thu, Jan 3rd', 'date': '2019-01-03', 'events': [], 'today': False},
                    {'day_formatted': 'Fri, Jan 4th', 'date': '2019-01-04', 'events': [
                        {'name': 'Future', 'time': '11:00', 'continued': False, 'date_range': 'Jan. 4, 2019 11:00',
                         'location': '',
                         'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                         'event_url': 'http://example.com/ccc/30c3/', 'subevent': se2.pk}], 'today': False},
                    {'day_formatted': 'Sat, Jan 5th', 'date': '2019-01-05', 'events': [], 'today': False},
                    {'day_formatted': 'Sun, Jan 6th', 'date': '2019-01-06', 'events': [], 'today': False}
                ],
            }

    def test_event_list(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                self.orga.events.create(name="Past", live=True, is_public=True, slug='past', date_from=now() - datetime.timedelta(days=3))
                self.orga.events.create(name="Present", live=True, is_public=True, slug='present', date_from=now())
                self.orga.events.create(name="Future", live=True, is_public=True, slug='future', date_from=now() + datetime.timedelta(days=3))
                self.orga.events.create(name="Disabled", live=False, is_public=True, slug='disabled', date_from=now() + datetime.timedelta(days=3))
                self.orga.events.create(name="Secret", live=True, is_public=False, slug='secret', date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Past", active=True, date_from=now() - datetime.timedelta(days=3))
                self.event.subevents.create(name="Present", active=True, date_from=now())
                self.event.subevents.create(name="Future", active=True, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Disabled", active=False, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))

            response = self.client.get('/%s/widget/product_list' % (self.orga.slug,))
            data = json.loads(response.content.decode())
            assert data == {
                'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
                'events': [
                    {'availability': {'color': 'none', 'text': 'Event series'},
                     'date_range': 'Dec. 29, 2018 – Jan. 4, 2019',
                     'event_url': 'http://example.com/ccc/30c3/',
                     'location': '',
                     'name': '30C3'},
                    {'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                     'date_range': 'Jan. 1, 2019 10:00',
                     'location': '',
                     'event_url': 'http://example.com/ccc/present/',
                     'name': 'Present'},
                    {'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                     'date_range': 'Jan. 4, 2019 10:00',
                     'location': '',
                     'event_url': 'http://example.com/ccc/future/',
                     'name': 'Future'}
                ],
                'list_type': 'list',
                'meta_filter_fields': [],
            }

    def test_event_list_filtersets_from_allowed_values(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                self.orga.meta_properties.create(
                    name="Language",
                    default="EN",
                    filter_public=True,
                    choices=[
                        {"key": "EN", "label": "English"},
                        {"key": "DE", "label": "German"},
                    ]
                )

            response = self.client.get('/%s/widget/product_list' % (self.orga.slug,))
            data = json.loads(response.content.decode())
            assert data["meta_filter_fields"] == [
                {
                    "choices": [["", ""], ["EN", "English"], ["DE", "German"]],
                    "key": "attr[Language]",
                    "label": "Language"
                }
            ]

    def test_event_list_filtersets_from_existing_values(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                p = self.orga.meta_properties.create(
                    name="Language",
                    default="DE",
                    filter_public=True,
                )
                e = self.orga.events.create(name="Future", live=True, is_public=True, slug='future', date_from=now() + datetime.timedelta(days=3))
                se = self.event.subevents.create(name="Future", active=True, date_from=now() + datetime.timedelta(days=3))
                e.meta_values.create(property=p, value="EN")
                se.meta_values.create(property=p, value="DE")

            response = self.client.get('/%s/widget/product_list' % (self.orga.slug,))
            data = json.loads(response.content.decode())
            assert data["meta_filter_fields"] == [
                {
                    "choices": [["", ""], ["DE", "DE"], ["EN", "EN"]],
                    "key": "attr[Language]",
                    "label": "Language"
                }
            ]

            response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
            data = json.loads(response.content.decode())
            assert data["meta_filter_fields"] == [
                {
                    "choices": [["", ""], ["DE", "DE"]],
                    "key": "attr[Language]",
                    "label": "Language"
                }
            ]

    def test_event_calendar(self):
        self.event.has_subevents = True
        self.event.settings.timezone = 'Europe/Berlin'
        self.event.save()
        with freeze_time("2019-01-01 10:00:00"):
            with scopes_disabled():
                self.orga.events.create(name="Past", live=True, is_public=True, slug='past', date_from=now() - datetime.timedelta(days=3))
                self.orga.events.create(name="Present", live=True, is_public=True, slug='present', date_from=now())
                self.orga.events.create(name="Future", live=True, is_public=True, slug='future', date_from=now() + datetime.timedelta(days=3))
                self.orga.events.create(name="Disabled", live=False, is_public=True, slug='disabled', date_from=now() + datetime.timedelta(days=3))
                self.orga.events.create(name="Secret", live=True, is_public=False, slug='secret', date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Past", active=True, date_from=now() - datetime.timedelta(days=3))
                se1 = self.event.subevents.create(name="Present", active=True, date_from=now())
                se2 = self.event.subevents.create(name="Future", active=True, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Disabled", active=False, date_from=now() + datetime.timedelta(days=3))
                self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))

            response = self.client.get('/%s/widget/product_list?style=calendar' % (self.orga.slug,))
            data = json.loads(response.content.decode())
            assert data == {
                'date': '2019-01-01',
                'list_type': 'calendar',
                'meta_filter_fields': [],
                'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
                'weeks': [
                    [None,
                     {'date': '2019-01-01',
                      'day': 1,
                      'events': [{'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                                  'continued': False,
                                  'date_range': 'Jan. 1, 2019 10:00',
                                  'event_url': 'http://example.com/ccc/present/',
                                  'name': 'Present',
                                  'location': '',
                                  'subevent': None,
                                  'time': '10:00'},
                                 {'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                                  'continued': False,
                                  'date_range': 'Jan. 1, 2019 11:00',
                                  'event_url': 'http://example.com/ccc/30c3/',
                                  'name': 'Present',
                                  'location': '',
                                  'subevent': se1.pk,
                                  'time': '11:00'}]},
                     {'date': '2019-01-02', 'day': 2, 'events': []},
                     {'date': '2019-01-03', 'day': 3, 'events': []},
                     {'date': '2019-01-04',
                      'day': 4,
                      'events': [{'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                                  'continued': False,
                                  'date_range': 'Jan. 4, 2019 10:00',
                                  'event_url': 'http://example.com/ccc/future/',
                                  'name': 'Future',
                                  'location': '',
                                  'subevent': None,
                                  'time': '10:00'},
                                 {'availability': {'color': 'none', 'text': 'More info', 'reason': 'unknown'},
                                  'continued': False,
                                  'date_range': 'Jan. 4, 2019 11:00',
                                  'event_url': 'http://example.com/ccc/30c3/',
                                  'name': 'Future',
                                  'location': '',
                                  'subevent': se2.pk,
                                  'time': '11:00'}]},
                     {'date': '2019-01-05', 'day': 5, 'events': []},
                     {'date': '2019-01-06', 'day': 6, 'events': []}],
                    [{'date': '2019-01-07', 'day': 7, 'events': []},
                     {'date': '2019-01-08', 'day': 8, 'events': []},
                     {'date': '2019-01-09', 'day': 9, 'events': []},
                     {'date': '2019-01-10', 'day': 10, 'events': []},
                     {'date': '2019-01-11', 'day': 11, 'events': []},
                     {'date': '2019-01-12', 'day': 12, 'events': []},
                     {'date': '2019-01-13', 'day': 13, 'events': []}],
                    [{'date': '2019-01-14', 'day': 14, 'events': []},
                     {'date': '2019-01-15', 'day': 15, 'events': []},
                     {'date': '2019-01-16', 'day': 16, 'events': []},
                     {'date': '2019-01-17', 'day': 17, 'events': []},
                     {'date': '2019-01-18', 'day': 18, 'events': []},
                     {'date': '2019-01-19', 'day': 19, 'events': []},
                     {'date': '2019-01-20', 'day': 20, 'events': []}],
                    [{'date': '2019-01-21', 'day': 21, 'events': []},
                     {'date': '2019-01-22', 'day': 22, 'events': []},
                     {'date': '2019-01-23', 'day': 23, 'events': []},
                     {'date': '2019-01-24', 'day': 24, 'events': []},
                     {'date': '2019-01-25', 'day': 25, 'events': []},
                     {'date': '2019-01-26', 'day': 26, 'events': []},
                     {'date': '2019-01-27', 'day': 27, 'events': []}],
                    [{'date': '2019-01-28', 'day': 28, 'events': []},
                     {'date': '2019-01-29', 'day': 29, 'events': []},
                     {'date': '2019-01-30', 'day': 30, 'events': []},
                     {'date': '2019-01-31', 'day': 31, 'events': []},
                     None,
                     None,
                     None]
                ]
            }
