# -*- coding: utf-8 -*-

import pytest

import datetime
import difflib
import dingguo
import ioex.datetimeex
import os
import pytz
import yaml

def get_figure_a():
    return dingguo.Figure(12.3, u'km')

def get_figure_b():
    return dingguo.Figure(12300, u'米')

def get_sum_a():
    return dingguo.Sum(1.23, u'EUR')

def get_sum_b():
    return dingguo.Sum(20.45, u'€')

def get_item_a():
    return dingguo.Item(
            name = u'item a',
            price_brutto = get_sum_a(),
            )

def get_item_b():
    return dingguo.Item(
            name = u'item β',
            price_brutto = get_sum_b(),
            )

def get_service():
    return dingguo.Service(
            name = u'service',
            price_brutto = dingguo.Sum(1.0, u'EUR'),
            duration = ioex.datetimeex.Duration(years = 2),
            )

def get_person_a():
    return dingguo.Person(
        first_name = u'Fabian Peter',
        last_name = u'Hammerle',
        )

def get_person_b():
    return dingguo.Person(
        first_name = u'名字',
        last_name = u'贵姓',
        )

def get_campaign_a():
    return dingguo.Campaign(
            name = u'campaign a',
            founder = u'company',
            end = datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo = pytz.timezone('Europe/Vienna')),
            )

def get_campaign_b():
    return dingguo.Campaign(
            founder = u'company',
            name = u'campaign without end',
            website_url = u'http://campaign.com',
            )

def get_pledge():
    return dingguo.Pledge(
            campaign = get_campaign_a(),
            price_brutto = dingguo.Sum(10.0, u'EUR'),
            reward = u'great',
            )

def get_contribution():
    return dingguo.Contribution(
            campaign = get_campaign_a(),
            price_brutto = dingguo.Sum(10.0, u'EUR'),
            reward = u'product',
            )

def get_article():
    return dingguo.Article(
            authors = ['a', 'b'],
            depth = dingguo.ScalarFigure(12.3, u'dm'),
            features = u'supergeil',
            height = dingguo.ScalarFigure(123., u'cm'),
            maximum_load = dingguo.ScalarFigure(40., u'kg'),
            name = u'article name',
            price_brutto = get_sum_a(),
            product_id = u'0815',
            quantity = 1,
            reseller = u'seller',
            shipper = u'shipper',
            state = u'goood',
            width = dingguo.ScalarFigure(1.23, u'm'),
            )

def get_transportation():
    return dingguo.Transportation(
            name = u'ticket',
            price_brutto = get_sum_a(),
            departure_point = u'home',
            destination_point = u'city',
            distance = dingguo.Distance(3.21, u'km'),
            passenger = get_person_a(),
            valid_from = datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.timezone('Europe/Vienna')),
            valid_until = datetime.datetime(2016, 7, 14, 18, 50, 4, 0, tzinfo = pytz.utc),
            ticket_url = u'https://www.example.com',
            estimated_arrival_time = ioex.datetimeex.Period(
                start = pytz.timezone('Europe/Vienna').localize(datetime.datetime(2016, 7, 24, 23, 47, 2)),
                end = pytz.utc.localize(datetime.datetime(2016, 7, 24, 23, 48, 5)),
                ),
            )

def get_shipping():
    return dingguo.Shipping(
            price_brutto = get_sum_a(),
            destination_point = u'home',
            )

def get_taxi_ride():
    return dingguo.TaxiRide(
            name = u'taxi ride',
            price_brutto = get_sum_a(),
            departure_point = u'home',
            destination_point = u'city',
            distance = dingguo.Distance(3.21, u'km'),
            driver = u'driver',
            arrival_time = datetime.datetime(2016, 5, 2, 18, 10, tzinfo = pytz.timezone('Europe/Vienna')),
            departure_time = datetime.datetime(2016, 5, 2, 18, 25, tzinfo = pytz.timezone('Europe/Vienna')),
            )

def get_discount_a():
    return dingguo.Discount(
            name = u'discount a',
            amount = get_sum_a(),
            )

def get_discount_b():
    return dingguo.Discount(
            name = u'discount β',
            amount = get_sum_b(),
            )

def get_order_a(items = True, discounts = True):
    order = dingguo.Order(
            platform = u'platformπ',
            order_id = u'id',
            order_date = datetime.datetime(2016, 5, 8, 0, 18, 17),
            customer_id = u'customer',
            )
    if items:
        order.items.append(get_item_a())
        order.items.append(get_item_b())
    if discounts:
        order.discounts.append(get_discount_a())
        order.discounts.append(get_discount_b())
    return order

def get_order_b():
    order = dingguo.Order(
            platform = u'platformπ',
            order_id = u'order_b',
            order_date = datetime.datetime(2015, 5, 8, 0, 18, 17),
            )
    return order

def get_order_c():
    order = dingguo.Order(
            platform = u'γάμμα',
            order_id = u'order_βήτα',
            order_date = datetime.datetime(2014, 5, 8, 0, 18, 17),
            customer_id = u'ρώ',
            )
    return order

def get_distance():
    return dingguo.Distance(2.4142, u'km')

def get_order_registry():
    registry = dingguo.OrderRegistry()
    registry.register(get_order_a(items = False, discounts = False))
    registry.register(get_order_b())
    registry.register(get_order_c())
    return registry

def to_yaml(data):
    return yaml.dump(data, default_flow_style = False, allow_unicode = True).decode('utf-8')

def yaml_diff(a, b):
    return '\n'.join(difflib.ndiff(
        to_yaml(a).split('\n'),
        to_yaml(b).split('\n'),
        ))

@pytest.mark.parametrize('source_object,expected_yaml', [
    [datetime.datetime(2016, 7, 14, 13, 50, 4, 0), '2016-07-14 13:50:04\n...\n'],
    [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.timezone('Europe/Vienna')), '2016-07-14 13:50:04+01:05\n...\n'],
    [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.utc), '2016-07-14 13:50:04+00:00\n...\n'],
    [dingguo.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
    [dingguo.ScalarFigure(1.34, u'μm'), u"!scalar '1.34 μm'\n"],
    [get_discount_a(), u"!discount\namount: !sum '1.23 EUR'\nname: discount a\n"],
    [get_discount_b(), u"!discount\namount: !sum '20.45 EUR'\nname: discount β\n"],
    [get_figure_a(), u'!figure\nunit: km\nvalue: 12.3\n'],
    [get_figure_b(), u'!figure\nunit: 米\nvalue: 12300\n'],
    [get_item_a(), u"!item\nname: item a\nprice_brutto: !sum '1.23 EUR'\n"],
    [get_item_b(), u"!item\nname: item β\nprice_brutto: !sum '20.45 EUR'\n"],
    [get_person_a(), u'!person\nfirst_name: Fabian Peter\nlast_name: Hammerle\n'],
    [get_person_b(), u'!person\nfirst_name: 名字\nlast_name: 贵姓\n'],
    [get_sum_a(), u"!sum '1.23 EUR'\n"],
    [get_sum_b(), u"!sum '20.45 EUR'\n"],
    [get_campaign_a(), u"""!campaign
end: 2016-07-23 09:23:17+01:05
founder: company
name: campaign a
"""],
    [get_campaign_b(), u"""!campaign
founder: company
name: campaign without end
website_url: http://campaign.com
"""],
    [get_service(), u"""!service
duration: !duration
  years: 2
name: service
price_brutto: !sum '1.0 EUR'
"""],
    [get_pledge(), u"""!pledge
campaign: !campaign
  end: 2016-07-23 09:23:17+01:05
  founder: company
  name: campaign a
price_brutto: !sum '10.0 EUR'
reward: great
"""],
    [get_contribution(), u"""!contribution
campaign: !campaign
  end: 2016-07-23 09:23:17+01:05
  founder: company
  name: campaign a
price_brutto: !sum '10.0 EUR'
reward: product
"""],
    [get_order_a(), u"""!order
customer_id: customer
discounts:
- !discount
  amount: !sum '1.23 EUR'
  name: discount a
- !discount
  amount: !sum '20.45 EUR'
  name: discount β
items:
- !item
  name: item a
  price_brutto: !sum '1.23 EUR'
- !item
  name: item β
  price_brutto: !sum '20.45 EUR'
order_date: 2016-05-08 00:18:17
order_id: id
platform: platformπ
"""],
    [get_article(), u"""!article
authors:
- a
- b
delivery_date: null
depth: !scalar '12.3 dm'
features: supergeil
height: !scalar '123.0 cm'
maximum_load: !scalar '40.0 kg'
name: article name
price_brutto: !sum '1.23 EUR'
product_id: 0815
quantity: 1
reseller: seller
shipper: shipper
state: goood
width: !scalar '1.23 m'
"""],
    [get_transportation(), u"""!transportation
departure_point: home
destination_point: city
distance: !distance '3.21 km'
estimated_arrival_time: !period
  end: 2016-07-24 23:48:05+00:00
  start: 2016-07-24 23:47:02+02:00
name: ticket
passenger: !person
  first_name: Fabian Peter
  last_name: Hammerle
price_brutto: !sum '1.23 EUR'
ticket_url: https://www.example.com
valid_from: 2016-07-14 13:50:04+01:05
valid_until: 2016-07-14 18:50:04+00:00
"""],
    [get_shipping(), u"""!shipping
destination_point: home
price_brutto: !sum '1.23 EUR'
"""],
    [get_taxi_ride(), u"""!taxi-ride
arrival_time: 2016-05-02 18:10:00+01:05
departure_point: home
departure_time: 2016-05-02 18:25:00+01:05
destination_point: city
distance: !distance '3.21 km'
driver: driver
name: taxi ride
price_brutto: !sum '1.23 EUR'
"""],
    [get_order_registry(), u"""!order-registry
platformπ:
  id: !order
    customer_id: customer
    discounts: []
    items: []
    order_date: 2016-05-08 00:18:17
    order_id: id
    platform: platformπ
  order_b: !order
    discounts: []
    items: []
    order_date: 2015-05-08 00:18:17
    order_id: order_b
    platform: platformπ
γάμμα:
  order_βήτα: !order
    customer_id: ρώ
    discounts: []
    items: []
    order_date: 2014-05-08 00:18:17
    order_id: order_βήτα
    platform: γάμμα
"""],
    ])
def test_to_yaml(source_object, expected_yaml):
    assert to_yaml(source_object) == expected_yaml

@pytest.mark.parametrize('expected_object,source_yaml', [
    [datetime.datetime(2016, 7, 14, 13, 50, 4, 0), '2016-07-14 13:50:04'],
    [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.timezone('Europe/Vienna')), '2016-07-14 13:50:04+01:05'],
    [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.utc), '2016-07-14 13:50:04+00:00'],
    [dingguo.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
    [dingguo.ScalarFigure(1.34, u'μm'), u"!scalar '1.34 μm'"],
    [get_discount_a(), u"!discount\nname: discount a\namount: !sum '1.23 EUR'\n"],
    [get_discount_b(), u"!discount\nname: discount β\namount: !sum '20.45 EUR'\n"],
    [get_figure_a(), '!figure\nunit: km\nvalue: 12.3\n'],
    [get_figure_a(), u'!figure\nunit: km\nvalue: 12.3\n'],
    [get_figure_b(), '!figure\nunit: 米\nvalue: 12300\n'],
    [get_figure_b(), u'!figure\nunit: 米\nvalue: 12300\n'],
    [get_item_a(), u"!item\nname: item a\nprice_brutto: !sum '1.23 EUR'\n"],
    [get_item_a(), u"!item\nname: item a\nprice_brutto: !sum 1.23 EUR"],
    [get_item_a(), u"!item\nname: item a\nprice_brutto: !sum 1.23 €\n"],
    [get_item_b(), u"!item\nname: item β\nprice_brutto: !sum '20.45 EUR'\n"],
    [get_person_a(), '!person\nfirst_name: Fabian Peter\nlast_name: Hammerle\n'],
    [get_person_a(), u'!person\nfirst_name: Fabian Peter\nlast_name: Hammerle\n'],
    [get_person_b(), u'!person\nfirst_name: 名字\nlast_name: 贵姓\n'],
    [get_sum_a(), "!sum 1.23 €"],
    [get_sum_a(), u"!sum '1.23 EUR'"],
    [get_sum_a(), u"!sum '1.23 EUR'"],
    [get_sum_a(), u"!sum 1.23 EUR"],
    [get_campaign_a(), u"""!campaign
name: campaign a
founder: company
end: 2016-07-23 09:23:17+01:05
"""],
    [get_campaign_b(), u"""!campaign
founder: company
name: campaign without end
website_url: http://campaign.com
"""],
    [get_service(), u"""!service
duration: !duration
  years: 2
name: service
price_brutto: !sum '1.0 EUR'
"""],
    [get_pledge(), u"""!pledge
campaign: !campaign
  name: campaign a
  founder: company
  end: 2016-07-23 09:23:17+01:05
price_brutto: !sum '10.0 EUR'
reward: great
"""],
    [get_contribution(), u"""!contribution
campaign: !campaign
  end: 2016-07-23 09:23:17+01:05
  founder: company
  name: campaign a
price_brutto: !sum '10.0 EUR'
reward: product
"""],
    [get_sum_a(), u"!sum 1.23 €"],
    [[get_person_a(), get_person_b()], u"""
- !person
  first_name: Fabian Peter
  last_name: Hammerle
- !person
  first_name: 名字
  last_name: 贵姓"""],
    [get_transportation(), u"""!transportation
departure_point: home
destination_point: city
distance: !distance '3.21 km'
name: ticket
price_brutto: !sum '1.23 EUR'
route_map: null
passenger: !person
  first_name: Fabian Peter
  last_name: Hammerle
ticket_url: https://www.example.com
valid_from: 2016-07-14 13:50:04+01:05
valid_until: 2016-07-14 18:50:04+00:00
estimated_arrival_time: !period
  end: 2016-07-24T23:48:05+00:00
  start: 2016-07-24T23:47:02+02:00
"""],
    [get_shipping(), u"""!shipping
destination_point: home
price_brutto: !sum 1.23 EUR
"""],
    ])
def test_from_yaml(expected_object, source_yaml):
    loaded_object = yaml.load(source_yaml)
    assert loaded_object == expected_object

def test_order_from_yaml():
    order_loaded = yaml.load(u"""!order
customer_id: customer
discounts:
- !discount
  amount: !sum '1.23 EUR'
  name: discount a
- !discount
  amount: !sum '20.45 EUR'
  name: discount β
items:
- !item
  name: item a
  price_brutto: !sum '1.23 EUR'
- !item
  name: item β
  price_brutto: !sum '20.45 EUR'
order_date: 2016-05-08 00:18:17
order_id: id
platform: platformπ
""")
    order_expected = get_order_a()
    assert order_expected == order_loaded, yaml_diff(order_expected, order_loaded)

def test_article_from_yaml():
    assert get_article() == yaml.load(u"""!article
authors:
- a
- b
delivery_date: null
features: supergeil
name: article name
price_brutto: !sum '1.23 EUR'
product_id: 0815
quantity: 1
reseller: seller
shipper: shipper
state: goood
depth: !scalar 12.3 dm
height: !scalar 123.0 cm
maximum_load: !scalar 40.0 kg
width: !scalar 1.23 m
""")

def test_taxi_ride_from_yaml():
    assert get_taxi_ride() == yaml.load(u"""!taxi-ride
arrival_time: 2016-05-02 18:10:00+01:05
departure_point: home
departure_time: 2016-05-02 18:25:00+01:05
destination_point: city
distance: !distance '3.21 km'
driver: driver
name: taxi ride
price_brutto: !sum '1.23 EUR'
route_map: null
""")

def test_order_registry_from_yaml():
    expected = get_order_registry()
    loaded = yaml.load(u"""!order-registry
platformπ:
  id: !order
    customer_id: customer
    discounts: []
    order_date: 2016-05-08 00:18:17
    order_id: id
    platform: platformπ
  order_b: !order
    customer_id: null
    items: []
    order_date: 2015-05-08 00:18:17
    order_id: order_b
    platform: platformπ
γάμμα:
  order_βήτα: !order
    customer_id: ρώ
    discounts: []
    items: []
    order_date: 2014-05-08 00:18:17
    order_id: order_βήτα
    platform: γάμμα
""")
    assert expected == loaded, yaml_diff(expected, loaded)