
import operator, random, six

from twisted.trial.unittest import TestCase

from axiom.iaxiom import IComparison, IColumn
from axiom.store import Store, ItemQuery, MultipleItemQuery
from axiom.item import Item, Placeholder

from axiom import errors
from axiom.attributes import (
    reference, text, bytes, integer, AND, OR, TableOrderComparisonWrapper)
from six.moves import map

class A(Item):
    schemaVersion = 1
    typeName = 'a'

    reftoc = reference()
    type = text(indexed=True)


class B(Item):
    schemaVersion = 1
    typeName = 'b'

    cref = reference()
    name = text(indexed=True)

class C(Item):
    schemaVersion = 1
    typeName = 'c'

    name = text(indexed=True)

class D(Item):
    schemaVersion = 1
    typeName = 'd'
    id = bytes()
    one = bytes()
    two = bytes()
    three = bytes()
    four = text()

class E(Item):
    schemaVersion = 1
    typeName = 'e'
    name = text()
    transaction = text()
    amount = integer()


class ThingWithCharacterAndByteStrings(Item):
    schemaVersion = 1

    typeName = 'ThingWithCharacterAndByteStrings'

    characterString = text(caseSensitive=True)
    caseInsensitiveCharString = text(caseSensitive=False)

    byteString = bytes()


class BasicQuery(TestCase):

    def test_rightHandStoreIDComparison(self):
        """
        Test that a StoreID column on the right-hand side of an equality test
        results in a TwoAttributeComparison object rather than an
        AttributeValueComparison or anything else that would be wrong.
        """
        s = Store()
        comparison = (A.reftoc == B.storeID)
        self.assertEqual(
            comparison.getQuery(s),
            '({}.[reftoc] = {}.oid)'.format(
                A.getTableName(s),
                B.getTableName(s)))
        self.assertEqual(comparison.getArgs(s), [])


    def test_leftHandStoreIDComparison(self):
        """
        Test that a StoreID column on the left-hand side of an equality test
        results in a TwoAttributeComparison object rather than an
        AttributeValueComparison or anything else that would be wrong.
        """
        s = Store()
        comparison = (B.storeID == A.reftoc)
        self.assertEqual(
            comparison.getQuery(s),
            '({}.oid = {}.[reftoc])'.format(
                B.getTableName(s),
                A.getTableName(s)))
        self.assertEqual(comparison.getArgs(s), [])


    def test_simplestQuery(self):
        """
        Test that an ItemQuery with no comparison, sorting, or limit generates
        the right SQL for that operation.
        """
        s = Store()
        query = ItemQuery(s, A)
        sql, args = query._sqlAndArgs('SELECT', '*')
        self.assertEqual(
            sql,
            'SELECT * FROM {}'.format(A.getTableName(s)))
        self.assertEquals(args, [])


    def test_simpleIntegerComparison(self):
        """
        Test that an ItemQuery with a single attribute comparison on an integer
        attribute generates SQL with the right WHERE clause.
        """
        s = Store()
        query = ItemQuery(s, E, E.amount == 0)
        sql, args = query._sqlAndArgs('SELECT', '*')
        self.assertEqual(
            sql,
            'SELECT * FROM {} WHERE ({}.[amount] = ?)'.format(
                E.getTableName(s),
                E.getTableName(s)))
        self.assertEqual(args, [0])


    def test_simpleReferenceComparison(self):
        """
        Test that an ItemQuery with a single attribute comparison on a
        reference attribute generates SQL with the right WHERE clause.
        """
        s = Store()
        query = ItemQuery(s, A, A.reftoc == A.storeID)
        sql, args = query._sqlAndArgs('SELECT', '*')
        self.assertEqual(
            sql,
            'SELECT * FROM {} WHERE ({}.[reftoc] = {}.oid)'.format(
                A.getTableName(s),
                A.getTableName(s),
                A.getTableName(s)))
        self.assertEqual(args, [])


    def test_reversedReferenceComparison(self):
        """
        Test that an ItemQuery with a single attribute comparison on a
        reference attribute with the storeID part on the left-hand side
        generates SQL with the right WHERE clause.
        """
        s = Store()
        query = ItemQuery(s, A, A.storeID == A.reftoc)
        sql, args = query._sqlAndArgs('SELECT', '*')
        self.assertEqual(
            sql,
            'SELECT * FROM {} WHERE ({}.oid = {}.[reftoc])'.format(
                A.getTableName(s),
                A.getTableName(s),
                A.getTableName(s)))
        self.assertEqual(args, [])


    def test_unionComparison(self):
        """
        Test that an ItemQuery with two comparisons joined with an L{AND}
        generates the right WHERE clause.
        """
        s = Store()
        query = ItemQuery(s, A, AND(A.reftoc == B.storeID,
                                    B.cref == C.storeID))
        sql, args = query._sqlAndArgs('SELECT', '*')
        self.assertEqual(
            sql,
            'SELECT * FROM %s, %s, %s '
            'WHERE ((%s.[reftoc] = %s.oid) AND '