"""
test special properties (eg. column_property, ...)
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import six

from sqlalchemy import select, func
from sqlalchemy.orm import column_property
from elixir import *

def setup():
    metadata.bind = 'sqlite://'


class TestSpecialProperties(object):
    def teardown(self):
        cleanup_all(True)

    def test_lifecycle(self):
        class A(Entity):
            name = Field(String(20))

        assert isinstance(A.name, Field)

        setup_all()

        assert not isinstance(A.name, Field)

    def test_generic_property(self):
        class Tag(Entity):
            score1 = Field(Float)
            score2 = Field(Float)

            score = GenericProperty(
                         lambda c: column_property(
                             (c.score1 * c.score2).label('score')))
        setup_all(True)

        t1 = Tag(score1=5.0, score2=3.0)
        t2 = Tag(score1=10.0, score2=2.0)

        session.commit()
        session.close()

        for tag in Tag.query.all():
            assert tag.score == tag.score1 * tag.score2

    def test_column_property(self):
        class Tag(Entity):
            score1 = Field(Float)
            score2 = Field(Float)

            score = ColumnProperty(lambda c: c.score1 * c.score2)

        setup_all(True)

        t1 = Tag(score1=5.0, score2=3.0)
        t2 = Tag(score1=10.0, score2=2.0)

        session.commit()
        session.close()

        for tag in Tag.query.all():
            assert tag.score == tag.score1 * tag.score2

    def test_column_property_eagerload_and_reuse(self):
        class Tag(Entity):
            score1 = Field(Float)
            score2 = Field(Float)

            user = ManyToOne('User')

            score = ColumnProperty(lambda c: c.score1 * c.score2)

        class User(Entity):
            name = Field(String(16))
            category = ManyToOne('Category')
            tags = OneToMany('Tag', lazy=False)
            score = ColumnProperty(lambda c:
                                   select([func.sum(Tag.score)],
                                          Tag.user_id == c.id).as_scalar())

        class Category(Entity):
            name = Field(String(16))
            users = OneToMany('User', lazy=False)

            score = ColumnProperty(lambda c:
                                   select([func.avg(User.score)],
                                          User.category_id == c.id
                                         ).as_scalar())
        setup_all(True)

        u1 = User(name='joe', tags=[Tag(score1=5.0, score2=3.0),
                                    Tag(score1=55.0, score2=1.0)])

        u2 = User(name='bar', tags=[Tag(score1=5.0, score2=4.0),
                                    Tag(score1=50.0, score2=1.0),
                                    Tag(score1=15.0, score2=2.0)])

        c1 = Category(name='dummy', users=[u1, u2])

        session.commit()
        session.close()

        category = Category.query.one()
        assert category.score == 85
        for user in category.users:
            assert user.score == sum([tag.score for tag in user.tags])
            for tag in user.tags:
                assert tag.score == tag.score1 * tag.score2

    def test_has_property(self):
        class Tag(Entity):
            has_field('score1', Float)
            has_field('score2', Float)
            has_property('score',
                         lambda c: column_property(
                             (c.score1 * c.score2).label('score')))

        setup_all(True)

        t1 = Tag(score1=5.0, score2=3.0)
        t1 = Tag(score1=10.0, score2=2.0)

        session.commit()
        session.close()