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

import pytest

import datetime
import difflib
import finoex
import ioex.calcex
import ioex.datetimeex
import os
import pytz
yaml = pytest.importorskip('yaml')

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

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

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

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

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

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

def get_service_a():
    return finoex.Service(
        name = u'service',
        price_brutto = finoex.Sum(1.0, u'EUR'),
        duration = ioex.datetimeex.Duration(years = 2),
        state = 'success',
        )

def get_service_b():
    return finoex.Service(
        name = u'service',
        price_brutto = finoex.Sum(1.0, u'EUR'),
        period = ioex.datetimeex.Period(
            start = datetime.datetime(2017, 4, 2, 10, 23),
            end = None,
            ),
        )

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

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

def get_campaign_a():
    return finoex.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 finoex.Campaign(
        founder = u'company',
        name = u'campaign without end',
        website_url = u'http://campaign.com',
        )

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

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

def get_article():
    return finoex.Article(
        authors = ['a', 'b'],
        depth = finoex.Distance(12.3, u'dm'),
        features = u'supergeil',
        height = finoex.Distance(123., u'cm'),
        maximum_load = ioex.calcex.Figure(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 = finoex.Distance(1.23, u'm'),
        )

def get_transportation():
    return finoex.Transportation(
            name = u'ticket',
            price_brutto = get_sum_a(),
            departure_point = u'home',
            destination_point = u'city',
            distance = finoex.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 finoex.Shipping(
            price_brutto = get_sum_a(),
            destination_point = u'home',
            )

def get_taxi_ride():
    return finoex.TaxiRide(
            name = u'taxi ride',
            price_brutto = get_sum_a(),
            departure_point = u'home',
            destination_point = u'city',
            distance = finoex.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 finoex.Discount(
            name = u'discount a',
            amount = get_sum_a(),
            )

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

def get_order_a(items = True, discounts = True):
    order = finoex.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 = finoex.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 = finoex.Order(
            platform = u'γάμμα',
            order_id = u'order_βήτα',
            order_date = datetime.datetime(2014, 5, 8, 0, 18, 17),
            customer_id = u'ρώ',
            )
    return order

def get_invoice(items = True):
    inv = finoex.Invoice(
            creditor = u'platformπ',
            invoice_id = u'id',
            invoice_date = datetime.datetime(2016, 5, 8, 0, 18, 17),
            debitor_id = u'customer',
            )
    if items:
        inv.items.append(get_item_a())
        inv.items.append(get_item_b())
    return inv

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

def to_yaml(data):
    class Dumper(yaml.Dumper):
        pass
    ioex.datetimeex.Duration.register_yaml_representer(yaml.Dumper)
    ioex.datetimeex.Period.register_yaml_representer(yaml.Dumper)
    ioex.calcex.Figure.register_yaml_representer(yaml.Dumper)
    finoex.Sum.register_yaml_representer(yaml.Dumper)
    finoex.Distance.register_yaml_representer(yaml.Dumper)
    return yaml.dump(
        data,
        default_flow_style = False,
        allow_unicode = True,
        Dumper = Dumper,
    ) #.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'],
    [finoex.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
    [finoex.Distance(1.34, u'μm'), u"!distance '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 '12.3 km'\n"],
    [get_figure_b(), u"!figure '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_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_a(), u"""!service
duration: !duration
  years: 2
name: service
price_brutto: !sum '1.0 EUR'
state: success
"""],
    [get_service_b(), u"""!service
name: service
period: !period
  end: null
  start: 2017-04-02 10:23:00
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_order_a(discounts = False), u"""!order
customer_id: customer
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
depth: !distance '12.3 dm'
features: supergeil
height: !distance '123.0 cm'
maximum_load: !figure '40.0 kg'
name: article name
price_brutto: !sum '1.23 EUR'
product_id: 0815
quantity: 1
reseller: seller
shipper: shipper
state: goood
width: !distance '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_invoice(), u"""!invoice
creditor: platformπ
debitor_id: customer
invoice_date: 2016-05-08 00:18:17
invoice_id: id
items:
- !item
  name: item a
  price_brutto: !sum '1.23 EUR'
- !item
  name: item β
  price_brutto: !sum '20.45 EUR'
"""],
    ])
def test_to_yaml(source_object, expected_yaml):
    assert expected_yaml == to_yaml(source_object)

@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'],
    [finoex.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
    [finoex.Distance(1.34, u'μm'), u"!distance '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_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_a(), u"""!service
duration: !duration
  years: 2
name: service
price_brutto: !sum '1.0 EUR'
state: success
"""],
    [get_service_b(), u"""!service
name: service
period: !period
  start: 2017-04-02 10:23:00
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_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
"""],
    [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
features: supergeil
name: article name
price_brutto: !sum '1.23 EUR'
product_id: 0815
quantity: 1
reseller: seller
shipper: shipper
state: goood
depth: !distance 12.3 dm
height: !distance 123.0 cm
maximum_load: !figure 40.0 kg
width: !distance 1.23 m
"""],
    [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'
route_map: null
"""],
    [get_invoice(), u"""!invoice
creditor: platformπ
debitor_id: customer
invoice_date: 2016-05-08T00:18:17
invoice_id: id
items:
- !item
  name: item a
  price_brutto: !sum '1.23 EUR'
- !item
  name: item β
  price_brutto: !sum '20.45 EUR'
"""],
    ])
def test_from_yaml(expected_object, source_yaml):
    class Loader(yaml.Loader):
        pass
    ioex.datetimeex.Duration.register_yaml_constructor(Loader)
    ioex.datetimeex.Period.register_yaml_constructor(Loader)
    ioex.calcex.Figure.register_yaml_constructor(Loader)
    finoex.Sum.register_yaml_constructor(Loader)
    finoex.Distance.register_yaml_constructor(Loader)
    loaded_object = yaml.load(source_yaml, Loader=Loader)
    assert loaded_object == expected_object