import json
from unittest.mock import MagicMock, Mock

from django.conf import settings
from django.forms import HiddenInput
from django.forms.models import model_to_dict
from django.urls import reverse
from prices import Money, MoneyRange, TaxedMoney, TaxedMoneyRange
from tests.utils import get_redirect_location

from saleor.dashboard.product import ProductBulkAction
from saleor.dashboard.product.forms import ProductForm, ProductVariantForm
from saleor.product.forms import VariantChoiceField
from saleor.product.models import (
    AttributeValue, Collection, Product, Attribute, ProductImage,
    ProductType, ProductVariant)

from ..utils import create_image


def test_view_product_list_with_filters(admin_client, product_list):
    url = reverse('dashboard:product-list')
    data = {
        'price_max': [''], 'price_min': [''], 'is_featured': [''],
        'name': ['Test'], 'sort_by': [''], 'is_published': ['']}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert list(response.context['filter_set'].qs) == product_list


def test_view_product_list_with_filters_sort_by(admin_client, product_list):
    url = reverse('dashboard:product-list')
    data = {
        'price_max': [''], 'price_min': [''], 'is_featured': [''],
        'name': ['Test'], 'sort_by': ['name'], 'is_published': ['']}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert list(response.context['filter_set'].qs) == product_list

    data['sort_by'] = ['-name']
    url = reverse('dashboard:product-list')

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert list(response.context['filter_set'].qs) == product_list[::-1]


def test_view_product_list_with_filters_is_published(
        admin_client, product_list, category):
    url = reverse('dashboard:product-list')
    data = {
        'price_max': [''], 'price_min': [''], 'is_featured': [''],
        'name': ['Test'], 'sort_by': ['name'], 'category': category.pk,
        'is_published': ['1']}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    result = list(response.context['filter_set'].qs)
    assert result == [product_list[0], product_list[2]]


def test_view_product_list_with_filters_no_results(admin_client, product_list):
    url = reverse('dashboard:product-list')
    data = {
        'price_max': [''], 'price_min': [''], 'is_featured': [''],
        'name': ['BADTest'], 'sort_by': [''], 'is_published': ['']}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert list(response.context['filter_set'].qs) == []


def test_view_product_list_pagination(admin_client, product_list):
    settings.DASHBOARD_PAGINATE_BY = 1
    url = reverse('dashboard:product-list')
    data = {'page': '1'}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert not response.context['filter_set'].is_bound_unsorted

    data = {'page': '2'}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert not response.context['filter_set'].is_bound_unsorted


def test_view_product_list_pagination_with_filters(admin_client, product_list):
    settings.DASHBOARD_PAGINATE_BY = 1
    url = reverse('dashboard:product-list')
    data = {
        'page': '1', 'price_max': [''], 'price_min': [''], 'is_featured': [''],
        'name': ['Test'], 'sort_by': ['name'], 'is_published': ['']}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert list(response.context['products'])[0] == product_list[0]

    data['page'] = '2'

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert list(response.context['products'])[0] == product_list[1]


def test_view_product_details(admin_client, product):
    price = TaxedMoney(net=Money(10, 'USD'), gross=Money(10, 'USD'))
    sale_price = TaxedMoneyRange(start=price, stop=price)
    purchase_cost = MoneyRange(start=Money(1, 'USD'), stop=Money(1, 'USD'))
    url = reverse('dashboard:product-details', kwargs={'pk': product.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    context = response.context
    assert context['product'] == product
    assert context['sale_price'] == sale_price
    assert context['purchase_cost'] == purchase_cost
    assert context['margin'] == (90, 90)


def test_view_product_toggle_publish(db, admin_client, product):
    url = reverse('dashboard:product-publish', kwargs={'pk': product.pk})
    expected_response = {'success': True, 'is_published': False}

    response = admin_client.post(url)

    assert response.status_code == 200
    assert json.loads(response.content.decode('utf8')) == expected_response
    product.refresh_from_db()
    assert not product.is_published

    admin_client.post(url)

    product.refresh_from_db()
    assert product.is_published


def test_view_product_select_type_display_modal(admin_client):
    url = reverse('dashboard:product-add-select-type')
    response = admin_client.get(url)
    assert response.status_code == 200


def test_view_product_select_type(admin_client, product_type):
    url = reverse('dashboard:product-add-select-type')
    data = {'product_type': product_type.pk}

    response = admin_client.post(url, data)

    assert get_redirect_location(response) == reverse(
        'dashboard:product-add', kwargs={'type_pk': product_type.pk})
    assert response.status_code == 302


def test_view_product_select_type_by_ajax(admin_client, product_type):
    url = reverse('dashboard:product-add-select-type')
    data = {'product_type': product_type.pk}

    response = admin_client.post(
        url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    assert response.status_code == 200
    resp_decoded = json.loads(response.content.decode('utf-8'))
    assert resp_decoded.get('redirectUrl') == reverse(
        'dashboard:product-add', kwargs={'type_pk': product_type.pk})


def test_view_product_create(admin_client, product_type, category):
    url = reverse('dashboard:product-add', kwargs={'type_pk': product_type.pk})
    data = {
        'name': 'Product', 'description': 'This is product description.',
        'price': 10, 'category': category.pk, 'variant-sku': '123',
        'variant-quantity': 2}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    product = Product.objects.first()
    assert get_redirect_location(response) == reverse(
        'dashboard:product-details', kwargs={'pk': product.pk})
    assert Product.objects.count() == 1


def test_view_product_edit(admin_client, product):
    url = reverse('dashboard:product-update', kwargs={'pk': product.pk})
    data = {
        'name': 'Product second name', 'description': 'Product description.',
        'price': 10, 'category': product.category.pk, 'variant-sku': '123',
        'variant-quantity': 10}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    product.refresh_from_db()
    assert get_redirect_location(response) == reverse(
        'dashboard:product-details', kwargs={'pk': product.pk})
    assert product.name == 'Product second name'


def test_view_product_delete(db, admin_client, product):
    url = reverse('dashboard:product-delete', kwargs={'pk': product.pk})

    response = admin_client.post(url)

    assert response.status_code == 302
    assert not Product.objects.filter(pk=product.pk)


def test_view_product_not_deleted_before_confirmation(
        db, admin_client, product):
    url = reverse('dashboard:product-delete', kwargs={'pk': product.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    product.refresh_from_db()


def test_view_product_bulk_update_publish(admin_client, product_list):
    url = reverse('dashboard:product-bulk-update')
    products =  [product.pk for product in product_list]
    data = {'action': ProductBulkAction.PUBLISH, 'products': products}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    assert get_redirect_location(response) == reverse('dashboard:product-list')

    for p in product_list:
        p.refresh_from_db()
        assert p.is_published


def test_view_product_bulk_update_unpublish(admin_client, product_list):
    url = reverse('dashboard:product-bulk-update')
    products =  [product.pk for product in product_list]
    data = {'action': ProductBulkAction.UNPUBLISH, 'products': products}

    response = admin_client.post(url, data)


def test_view_ajax_products_list(admin_client, product):
    url = reverse('dashboard:ajax-products')

    response = admin_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    assert response.status_code == 200
    resp_decoded = json.loads(response.content.decode('utf-8'))
    assert resp_decoded.get('results') == [
        {'id': product.id, 'text': str(product)}]


def test_view_product_type_list(admin_client, product_type):
    url = reverse('dashboard:product-type-list')

    response = admin_client.get(url)

    assert response.status_code == 200
    assert len(response.context['product_types']) == 1


def test_view_product_type_list_with_filters(admin_client, product_type):
    url = reverse('dashboard:product-type-list')
    data = {'name': ['Default Ty'], 'sort_by': ['']}

    response = admin_client.get(url, data)

    assert response.status_code == 200
    assert product_type in response.context['filter_set'].qs
    assert len(response.context['filter_set'].qs) == 1


def test_view_product_type_create(
        admin_client, color_attribute, size_attribute):
    url = reverse('dashboard:product-type-add')
    data = {
        'name': 'Testing Type',
        'product_attributes': [color_attribute.pk],
        'variant_attributes': [size_attribute.pk],
        'has_variants': True,
        'weight': ['3.47']}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    assert get_redirect_location(response) == reverse(
        'dashboard:product-type-list')
    assert ProductType.objects.count() == 1


def test_view_product_type_create_invalid(
        admin_client, color_attribute, size_attribute):
    url = reverse('dashboard:product-type-add')
    # Don't allow same attribute in both fields
    data = {
        'name': 'Testing Type',
        'product_attributes': [size_attribute.pk],
        'variant_attributes': [color_attribute.pk, size_attribute.pk],
        'has_variants': True,
        'weight': ['3.47']}

    response = admin_client.post(url, data)

    assert response.status_code == 200
    assert ProductType.objects.count() == 0


def test_view_product_type_create_missing_variant_attributes(
        admin_client, color_attribute, size_attribute):
    url = reverse('dashboard:product-type-add')
    data = {
        'name': 'Testing Type',
        'product_attributes': [color_attribute.pk],
        'variant_attributes': [size_attribute.pk],
        'has_variants': False,
        'weight': ['3.47']}
    response = admin_client.post(url, data)

    assert response.status_code == 200
    assert ProductType.objects.count() == 0


def test_view_product_type_create_variantless(
        admin_client, color_attribute, size_attribute):
    url = reverse('dashboard:product-type-add')
    data = {
        'name': 'Testing Type',
        'product_attributes': [color_attribute.pk],
        'variant_attributes': [],
        'has_variants': False,
        'weight': ['3.47']}
    response = admin_client.post(url, data)

    assert response.status_code == 302
    assert get_redirect_location(response) == reverse(
        'dashboard:product-type-list')
    assert ProductType.objects.count() == 1


def test_view_product_type_create_variantless_invalid(
        admin_client, color_attribute, size_attribute):
    url = reverse('dashboard:product-type-add')
    # Don't allow variant attributes when no variants
    data = {
        'name': 'Testing Type',
        'product_attributes': [color_attribute.pk],
        'variant_attributes': [size_attribute.pk],
        'has_variants': False,
        'weight': ['3.47']}
    response = admin_client.post(url, data)

    assert response.status_code == 200
    assert ProductType.objects.count() == 0


def test_view_product_type_edit_to_no_variants_valid(admin_client, product):
    product_type = ProductType.objects.create(
        name='New product type', has_variants=True)
    product.product_type = product_type
    product.save()

    url = reverse(
        'dashboard:product-type-update', kwargs={'pk': product_type.pk})
    # When all products have only one variant you can change
    # has_variants to false
    data = {
        'name': product_type.name,
        'product_attributes': product_type.product_attributes.values_list(
            'pk', flat=True),
        'variant_attributes': product_type.variant_attributes.values_list(
            'pk', flat=True),
        'has_variants': False,
        'weight': ['3.47']}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    assert get_redirect_location(response) == url
    product_type.refresh_from_db()
    assert not product_type.has_variants
    assert product.variants.count() == 1


def test_view_product_type_edit_to_no_variants_invalid(admin_client, product):
    product_type = ProductType.objects.create(
        name='New product type', has_variants=True)
    product.product_type = product_type
    product.save()

    product.variants.create(sku='12345')

    url = reverse(
        'dashboard:product-type-update', kwargs={'pk': product_type.pk})
    # Test has_variants validator which prevents turning off when product
    # has multiple variants
    data = {
        'name': product_type.name,
        'product_attributes': product_type.product_attributes.values_list(
            'pk', flat=True),
        'variant_attributes': product_type.variant_attributes.values_list(
            'pk', flat=True),
        'has_variants': False,
        'weight': ['3.47']}

    response = admin_client.post(url, data)

    assert response.status_code == 200
    product_type.refresh_from_db()
    assert product_type.has_variants
    assert product.variants.count() == 2


def test_view_product_type_delete(db, admin_client, product):
    product_type = product.product_type
    url = reverse(
        'dashboard:product-type-delete', kwargs={'pk': product_type.pk})

    response = admin_client.post(url)

    assert response.status_code == 302
    assert not ProductType.objects.filter(pk=product_type.pk)


def test_view_product_type_not_deleted_before_confirmation(
        admin_client, product):
    product_type = product.product_type
    url = reverse(
        'dashboard:product-type-delete', kwargs={'pk': product_type.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert ProductType.objects.filter(pk=product_type.pk)


def test_view_product_variant_details(admin_client, product):
    product_type = product.product_type
    product_type.has_variants = True
    product_type.save()
    variant = product.variants.get()
    url = reverse(
        'dashboard:variant-details',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    context = response.context
    assert context['product'] == product
    assert context['variant'] == variant
    assert context['images'].count() == 0
    assert context['margin'] == 90
    assert context['discounted_price'] == variant.base_price


def test_view_product_variant_details_redirect_to_product(
        admin_client, product):
    variant = product.variants.get()
    url = reverse(
        'dashboard:variant-details',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})

    response = admin_client.get(url)

    assert response.status_code == 302
    assert get_redirect_location(response) == reverse(
        'dashboard:product-details', kwargs={'pk': product.pk})


def test_view_product_variant_create(admin_client, product):
    product_type = product.product_type
    product_type.has_variants = True
    product_type.save()
    url = reverse('dashboard:variant-add', kwargs={'product_pk': product.pk})
    data = {
        'sku': 'ABC', 'price_override': '', 'quantity': 10, 'cost_price': ''}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    variant = product.variants.last()
    assert get_redirect_location(response) == reverse(
        'dashboard:variant-details',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})
    assert product.variants.count() == 2
    assert variant.sku == 'ABC'


def test_view_product_variant_edit(admin_client, product):
    variant = product.variants.get()
    url = reverse(
        'dashboard:variant-update',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})
    data = {
        'sku': 'ABC', 'price_override': '', 'quantity': 10, 'cost_price': ''}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    variant = product.variants.last()
    assert get_redirect_location(response) == reverse(
        'dashboard:variant-details',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})
    assert variant.sku == 'ABC'


def test_view_product_variant_delete(admin_client, product):
    variant = product.variants.get()
    url = reverse(
        'dashboard:variant-delete',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})

    response = admin_client.post(url)
    assert response.status_code == 302

    assert not ProductVariant.objects.filter(pk=variant.pk).exists()


def test_view_product_variant_not_deleted_before_confirmation(
        admin_client, product):
    variant = product.variants.get()
    url = reverse(
        'dashboard:variant-delete',
        kwargs={'product_pk': product.pk, 'variant_pk': variant.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert ProductVariant.objects.filter(pk=variant.pk).exists()


def test_view_variant_images(admin_client, product_with_image):
    variant = product_with_image.variants.get()
    product_image = product_with_image.images.get()
    url = reverse(
        'dashboard:variant-images',
        kwargs={'product_pk': product_with_image.pk, 'variant_pk': variant.pk})
    data = {'images': [product_image.pk]}

    response = admin_client.post(url, data)

    assert response.status_code == 302
    assert get_redirect_location(response) == reverse(
        'dashboard:variant-details',
        kwargs={'product_pk': product_with_image.pk, 'variant_pk': variant.pk})
    assert variant.variant_images.filter(image=product_image).exists()


def test_view_ajax_available_variants_list(admin_client, product, category):
    unavailable_product = Product.objects.create(
        name='Test product', price=Money(10, settings.DEFAULT_CURRENCY),
        product_type=product.product_type,
        category=category, is_published=False)
    unavailable_product.variants.create()
    url = reverse('dashboard:ajax-available-variants')

    response = admin_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    assert response.status_code == 200
    resp_decoded = json.loads(response.content.decode('utf-8'))
    variant = product.variants.get()
    assert resp_decoded.get('results') == [
        {'id': variant.id, 'text': variant.get_ajax_label()}]


def test_view_product_images(admin_client, product_with_image):
    product_image = product_with_image.images.get()
    url = reverse(
        'dashboard:product-image-list',
        kwargs={'product_pk': product_with_image.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert response.context['product'] == product_with_image
    assert not response.context['is_empty']
    images = response.context['images']
    assert len(images) == 1
    assert product_image in images


def test_view_product_image_create(
        monkeypatch, admin_client, product_with_image):
    mock_create_thumbnails = Mock(return_value=None)
    monkeypatch.setattr(
        'saleor.dashboard.product.forms.create_product_thumbnails.delay',
        mock_create_thumbnails)
    url = reverse(
        'dashboard:product-image-add',
        kwargs={'product_pk': product_with_image.pk})

    response = admin_client.get(url)

    assert response.status_code == 200

    image, image_name = create_image()
    data = {'image_0': image, 'alt': ['description']}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    assert ProductImage.objects.count() == 2
    product_with_image.refresh_from_db()
    images = product_with_image.images.all()
    assert len(images) == 2
    assert image_name in images[1].image.name
    assert images[1].alt == 'description'
    mock_create_thumbnails.assert_called_once_with(images[1].pk)


def test_view_product_image_edit_same_image_add_description(
        monkeypatch, admin_client, product_with_image):
    mock_create_thumbnails = Mock(return_value=None)
    monkeypatch.setattr(
        'saleor.dashboard.product.forms.create_product_thumbnails.delay',
        mock_create_thumbnails)
    product_image = product_with_image.images.all()[0]
    url = reverse(
        'dashboard:product-image-update',
        kwargs={
            'img_pk': product_image.pk,
            'product_pk': product_with_image.pk})
    data = {'image_1': ['0.49x0.59'], 'alt': ['description']}

    response = admin_client.get(url)

    assert response.status_code == 200

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    assert product_with_image.images.count() == 1
    product_image.refresh_from_db()
    assert product_image.alt == 'description'
    mock_create_thumbnails.assert_called_once_with(product_image.pk)


def test_view_product_image_edit_new_image(
        monkeypatch, admin_client, product_with_image):
    mock_create_thumbnails = Mock(return_value=None)
    monkeypatch.setattr(
        'saleor.dashboard.product.forms.create_product_thumbnails.delay',
        mock_create_thumbnails)
    product_image = product_with_image.images.all()[0]
    url = reverse(
        'dashboard:product-image-update',
        kwargs={
            'img_pk': product_image.pk,
            'product_pk': product_with_image.pk})

    response = admin_client.get(url)

    assert response.status_code == 200

    image, image_name = create_image()
    data = {'image_0': image, 'alt': ['description']}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    assert product_with_image.images.count() == 1
    product_image.refresh_from_db()
    assert image_name in product_image.image.name
    assert product_image.alt == 'description'
    mock_create_thumbnails.assert_called_once_with(product_image.pk)


def test_view_product_image_delete(admin_client, product_with_image):
    product_image = product_with_image.images.all()[0]
    url = reverse(
        'dashboard:product-image-delete',
        kwargs={
            'img_pk': product_image.pk,
            'product_pk': product_with_image.pk})

    response = admin_client.post(url)

    assert response.status_code == 302
    assert not ProductImage.objects.filter(pk=product_image.pk)


def test_view_product_image_not_deleted_before_confirmation(
        admin_client, product_with_image):
    product_image = product_with_image.images.all()[0]
    url = reverse(
        'dashboard:product-image-delete',
        kwargs={
            'img_pk': product_image.pk,
            'product_pk': product_with_image.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert ProductImage.objects.filter(pk=product_image.pk).count()


def test_view_ajax_reorder_product_images(admin_client, product_with_images):
    order_before = [img.pk for img in product_with_images.images.all()]
    ordered_images = list(reversed(order_before))
    url = reverse(
        'dashboard:product-images-reorder',
        kwargs={'product_pk': product_with_images.pk})
    data = {'ordered_images': ordered_images}

    response = admin_client.post(
        url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    assert response.status_code == 200
    order_after = [img.pk for img in product_with_images.images.all()]
    assert order_after == ordered_images


def test_view_ajax_reorder_product_images_invalid(
        admin_client, product_with_images):
    order_before = [img.pk for img in product_with_images.images.all()]
    ordered_images = list(reversed(order_before)).append(3)
    url = reverse(
        'dashboard:product-images-reorder',
        kwargs={'product_pk': product_with_images.pk})
    data = {'ordered_images': ordered_images}

    response = admin_client.post(
        url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    assert response.status_code == 400
    resp_decoded = json.loads(response.content.decode('utf-8'))
    assert 'error' in resp_decoded
    assert 'ordered_images' in resp_decoded['error']


def test_view_ajax_upload_image(monkeypatch, admin_client, product_with_image):
    mock_create_thumbnails = Mock(return_value=None)
    monkeypatch.setattr(
        'saleor.dashboard.product.forms.create_product_thumbnails.delay',
        mock_create_thumbnails)
    product = product_with_image
    url = reverse(
        'dashboard:product-images-upload', kwargs={'product_pk': product.pk})
    image, image_name = create_image()
    data = {'image_0': image, 'alt': ['description']}

    response = admin_client.post(
        url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    assert response.status_code == 200
    assert ProductImage.objects.count() == 2
    product_with_image.refresh_from_db()
    images = product_with_image.images.all()
    assert len(images) == 2
    assert image_name in images[1].image.name
    mock_create_thumbnails.assert_called_once_with(images[1].pk)


def test_view_attribute_list_no_results(admin_client):
    url = reverse('dashboard:attributes')
    response = admin_client.get(url)
    assert response.status_code == 200
    assert response.context['attributes'].object_list == []


def test_view_attribute_list(db, admin_client, color_attribute):
    url = reverse('dashboard:attributes')

    response = admin_client.get(url)

    assert response.status_code == 200
    result = response.context['attributes'].object_list
    assert len(result) == 1
    assert result[0][0] == color_attribute.pk
    assert result[0][1] == color_attribute.name
    assert len(result[0][3]) == 2
    assert not response.context['is_empty']


def test_view_attribute_details(admin_client, color_attribute):
    url = reverse(
        'dashboard:attribute-details',
        kwargs={'pk': color_attribute.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert response.context['attribute'] == color_attribute


def test_view_attribute_details_no_choices(admin_client):
    attribute = Attribute.objects.create(slug='size', name='Size')
    url = reverse(
        'dashboard:attribute-details', kwargs={'pk': attribute.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert response.context['attribute'] == attribute


def test_view_attribute_create(admin_client, color_attribute):
    url = reverse('dashboard:attribute-add')
    data = {'name': 'test', 'slug': 'test'}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    assert Attribute.objects.count() == 2


def test_view_attribute_create_not_valid(admin_client, color_attribute):
    url = reverse('dashboard:attribute-add')
    data = {}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    assert Attribute.objects.count() == 1


def test_view_attribute_edit(color_attribute, admin_client):
    url = reverse(
        'dashboard:attribute-update',
        kwargs={'pk': color_attribute.pk})
    data = {'name': 'new_name', 'slug': 'new_slug'}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    assert Attribute.objects.count() == 1
    color_attribute.refresh_from_db()
    assert color_attribute.name == 'new_name'
    assert color_attribute.slug == 'new_slug'


def test_view_attribute_delete(admin_client, color_attribute):
    url = reverse(
        'dashboard:attribute-delete',
        kwargs={'pk': color_attribute.pk})

    response = admin_client.post(url)

    assert response.status_code == 302
    assert not Attribute.objects.filter(pk=color_attribute.pk).exists()


def test_view_attribute_not_deleted_before_confirmation(
        admin_client, color_attribute):
    url = reverse(
        'dashboard:attribute-delete',
        kwargs={'pk': color_attribute.pk})

    response = admin_client.get(url)

    assert response.status_code == 200
    assert Attribute.objects.filter(pk=color_attribute.pk)


def test_view_attribute_value_create(color_attribute, admin_client):
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert values.count() == 2
    url = reverse(
        'dashboard:attribute-value-add',
        kwargs={'attribute_pk': color_attribute.pk})
    data = {'name': 'Pink', 'attribute': color_attribute.pk}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert values.count() == 3


def test_view_attribute_value_create_invalid(
        color_attribute, admin_client):
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert values.count() == 2
    url = reverse(
        'dashboard:attribute-value-add',
        kwargs={'attribute_pk': color_attribute.pk})
    data = {}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert values.count() == 2


def test_view_attribute_value_edit(color_attribute, admin_client):
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert values.count() == 2
    url = reverse(
        'dashboard:attribute-value-update',
        kwargs={'attribute_pk': color_attribute.pk, 'value_pk': values[0].pk})
    data = {'name': 'Pink', 'attribute': color_attribute.pk}

    response = admin_client.post(url, data, follow=True)

    assert response.status_code == 200
    values = AttributeValue.objects.filter(
        attribute=color_attribute.pk, name='Pink')
    assert len(values) == 1
    assert values[0].name == 'Pink'


def test_view_attribute_value_delete(color_attribute, admin_client):
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert values.count() == 2
    deleted_value = values[0]
    url = reverse(
        'dashboard:attribute-value-delete',
        kwargs={
            'attribute_pk': color_attribute.pk, 'value_pk': deleted_value.pk})

    response = admin_client.post(url, follow=True)

    assert response.status_code == 200
    values = AttributeValue.objects.filter(attribute=color_attribute.pk)
    assert len(values) == 1
    assert deleted_value not in values


def test_view_ajax_reorder_attribute_values(
        admin_client, color_attribute):
    order_before = [val.pk for val in color_attribute.values.all()]
    ordered_values = list(reversed(order_before))
    url = reverse(
        'dashboard:attribute-values-reorder',
        kwargs={'attribute_pk': color_attribute.pk})
    data = {'ordered_values': ordered_values}
    response = admin_client.post(
        url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
    order_after = [val.pk for val in color_attribute.values.all()]
    assert response.status_code == 200
    assert order_after == ordered_values


def test_view_ajax_reorder_attribute_values_invalid(
        admin_client, color_attribute):
    order_before = [val.pk for val in color_attribute.values.all()]
    ordered_values = list(reversed(order_before)).append(3)
    url = reverse(
        'dashboard:attribute-values-reorder',
        kwargs={'attribute_pk': color_attribute.pk})
    data = {'ordered_values': ordered_values}
    response = admin_client.post(
        url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
    assert response.status_code == 400
    resp_decoded = json.loads(response.content.decode('utf-8'))
    assert 'error' in resp_decoded
    assert 'ordered_values' in resp_decoded['error']


def test_get_formfield_name_with_unicode_characters(db):
    text_attribute = Attribute.objects.create(
        slug='ąęαβδηθλμπ', name='ąęαβδηθλμπ')
    assert text_attribute.get_formfield_name() == 'attribute-ąęαβδηθλμπ'


def test_product_variant_form(product):
    variant = product.variants.first()
    variant.name = ''
    variant.save()
    example_size = 'Small Size'
    data = {'attribute-size': example_size, 'sku': '1111', 'quantity': 2}

    form = ProductVariantForm(data, instance=variant)
    assert form.is_valid()

    form.save()
    variant.refresh_from_db()
    assert variant.name == example_size


def test_hide_field_in_variant_choice_field_form():
    form = VariantChoiceField(Mock())
    variants, cart = MagicMock(), MagicMock()
    variants.count.return_value = variants.all().count.return_value = 1
    variants.all()[0].pk = 'test'

    form.update_field_data(variants, discounts=None, taxes=None)

    assert isinstance(form.widget, HiddenInput)
    assert form.widget.attrs.get('value') == 'test'


def test_product_form_change_attributes(db, product, color_attribute):
    product_type = product.product_type
    text_attribute = Attribute.objects.create(
        slug='author', name='Author')
    product_type.product_attributes.add(text_attribute)
    color_value = color_attribute.values.first()
    new_author = 'Main Tester'
    data = {
        'name': product.name,
        'price': product.price.amount,
        'category': product.category.pk,
        'description': 'description',
        'attribute-author': new_author,
        'attribute-color': color_value.pk}

    form = ProductForm(data, instance=product)
    assert form.is_valid()

    product = form.save()
    assert product.attributes[str(color_attribute.pk)] == str(color_value.pk)

    # Check that new attribute was created for author
    author_value = AttributeValue.objects.get(name=new_author)
    assert product.attributes[str(text_attribute.pk)] == str(author_value.pk)


def test_product_form_assign_collection_to_product(product):
    collection = Collection.objects.create(name='test_collections')
    data = {
        'name': product.name,
        'price': product.price.amount,
        'category': product.category.pk,
        'description': 'description',
        'collections': [collection.pk]}

    form = ProductForm(data, instance=product)
    assert form.is_valid()

    form.save()
    assert product.collections.first().name == 'test_collections'
    assert collection.products.first().name == product.name


def test_product_form_sanitize_product_description(product_type, category):
    product = Product.objects.create(
        name='Test Product', price=Money(10, settings.DEFAULT_CURRENCY),
        description='', pk=10, product_type=product_type, category=category)
    data = model_to_dict(product)
    data['description'] = (
        '<b>bold</b><p><i>italic</i></p><h2>Header</h2><h3>subheader</h3>'
        '<blockquote>quote</blockquote>'
        '<p><a href="www.mirumee.com">link</a></p>'
        '<p>an <script>evil()</script>example</p>')
    data['price'] = 20

    form = ProductForm(data, instance=product)
    assert form.is_valid()

    form.save()
    assert product.description == (
        '<b>bold</b><p><i>italic</i></p><h2>Header</h2><h3>subheader</h3>'
        '<blockquote>quote</blockquote>'
        '<p><a href="www.mirumee.com">link</a></p>'
        '<p>an &lt;script&gt;evil()&lt;/script&gt;example</p>')
    assert product.seo_description == (
        'bolditalicHeadersubheaderquotelinkan evil()example')


def test_product_form_seo_description(unavailable_product):
    seo_description = (
        'This is a dummy product. '
        'HTML <b>shouldn\'t be removed</b> since it\'s a simple text field.')
    data = model_to_dict(unavailable_product)
    data['price'] = 20
    data['description'] = 'a description'
    data['seo_description'] = seo_description

    form = ProductForm(data, instance=unavailable_product)
    assert form.is_valid()

    form.save()
    assert unavailable_product.seo_description == seo_description


def test_product_form_seo_description_too_long(unavailable_product):
    description = (
        'Saying it fourth made saw light bring beginning kind over herb '
        'won\'t creepeth multiply dry rule divided fish herb cattle greater '
        'fly divided midst, gathering can\'t moveth seed greater subdue. '
        'Lesser meat living fowl called. Dry don\'t wherein. Doesn\'t above '
        'form sixth. Image moving earth without forth light whales. Seas '
        'were first form fruit that form they\'re, shall air. And. Good of'
        'signs darkness be place. Was. Is form it. Whose. Herb signs stars '
        'fill own fruit wherein. '
        'Don\'t set man face living fifth Thing the whales were. '
        'You fish kind. '
        'Them, his under wherein place first you night gathering.')

    data = model_to_dict(unavailable_product)
    data['price'] = 20
    data['description'] = description

    form = ProductForm(data, instance=unavailable_product)
    assert form.is_valid()

    form.save()
    new_seo_description = unavailable_product.seo_description
    assert len(new_seo_description) <= 300
    assert new_seo_description.startswith(
        'Saying it fourth made saw light bring beginning kind over herb '
        'won\'t creepeth multiply dry rule divided fish herb cattle greater '
        'fly divided midst, gathering can\'t moveth seed greater subdue. '
        'Lesser meat living fowl called. Dry don\'t wherein. Doesn\'t above '
        'form sixth. Image moving earth without')
    assert (
        new_seo_description.endswith('...') or new_seo_description[-1] == '…')
