import sqlalchemy as sa
from sqlalchemy import Integer, String, ForeignKey, event
from sqlalchemy import testing
from sqlalchemy.testing.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import eq_


class TriggerDefaultsTest(fixtures.MappedTest):
    __requires__ = ('row_triggers',)

    @classmethod
    def define_tables(cls, metadata):
        dt = Table('dt', metadata,
                   Column('id', Integer, primary_key=True,
                          test_needs_autoincrement=True),
                   Column('col1', String(20)),
                   Column('col2', String(20),
                          server_default=sa.schema.FetchedValue()),
                   Column('col3', String(20),
                          sa.schema.FetchedValue(for_update=True)),
                   Column('col4', String(20),
                          sa.schema.FetchedValue(),
                          sa.schema.FetchedValue(for_update=True)))
        for ins in (
            sa.DDL("CREATE TRIGGER dt_ins AFTER INSERT ON dt "
                   "FOR EACH ROW BEGIN "
                   "UPDATE dt SET col2='ins', col4='ins' "
                   "WHERE dt.id = NEW.id; END",
                   on='sqlite'),
            sa.DDL("CREATE TRIGGER dt_ins ON dt AFTER INSERT AS "
                   "UPDATE dt SET col2='ins', col4='ins' "
                   "WHERE dt.id IN (SELECT id FROM inserted);",
                   on='mssql'),
            sa.DDL("CREATE TRIGGER dt_ins BEFORE INSERT "
                   "ON dt "
                   "FOR EACH ROW "
                   "BEGIN "
                   ":NEW.col2 := 'ins'; :NEW.col4 := 'ins'; END;",
                   on='oracle'),
            sa.DDL("CREATE TRIGGER dt_ins BEFORE INSERT ON dt "
                   "FOR EACH ROW BEGIN "
                   "SET NEW.col2='ins'; SET NEW.col4='ins'; END",
                   on=lambda ddl, event, target, bind, **kw:
                   bind.engine.name not in ('oracle', 'mssql', 'sqlite')
                   ),
        ):
            event.listen(dt, 'after_create', ins)

        event.listen(dt, 'before_drop', sa.DDL("DROP TRIGGER dt_ins"))

        for up in (
            sa.DDL("CREATE TRIGGER dt_up AFTER UPDATE ON dt "
                   "FOR EACH ROW BEGIN "
                   "UPDATE dt SET col3='up', col4='up' "
                   "WHERE dt.id = OLD.id; END",
                   on='sqlite'),
            sa.DDL("CREATE TRIGGER dt_up ON dt AFTER UPDATE AS "
                   "UPDATE dt SET col3='up', col4='up' "
                   "WHERE dt.id IN (SELECT id FROM deleted);",
                   on='mssql'),
            sa.DDL("CREATE TRIGGER dt_up BEFORE UPDATE ON dt "
                   "FOR EACH ROW BEGIN "
                   ":NEW.col3 := 'up'; :NEW.col4 := 'up'; END;",
                   on='oracle'),
            sa.DDL("CREATE TRIGGER dt_up BEFORE UPDATE ON dt "
                   "FOR EACH ROW BEGIN "
                   "SET NEW.col3='up'; SET NEW.col4='up'; END",
                   on=lambda ddl, event, target, bind, **kw:
                   bind.engine.name not in ('oracle', 'mssql', 'sqlite')
                   ),
        ):
            event.listen(dt, 'after_create', up)

        event.listen(dt, 'before_drop', sa.DDL("DROP TRIGGER dt_up"))

    @classmethod
    def setup_classes(cls):
        class Default(cls.Comparable):
            pass

    @classmethod
    def setup_mappers(cls):
        Default, dt = cls.classes.Default, cls.tables.dt

        mapper(Default, dt)

    def test_insert(self):
        Default = self.classes.Default

        d1 = Default(id=1)

        eq_(d1.col1, None)
        eq_(d1.col2, None)
        eq_(d1.col3, None)
        eq_(d1.col4, None)

        session = create_session()
        session.add(d1)
        session.flush()

        eq_(d1.col1, None)
        eq_(d1.col2, 'ins')
        eq_(d1.col3, None)
        # don't care which trigger fired
        assert d1.col4 in ('ins', 'up')

    def test_update(self):
        Default = self.classes.Default

        d1 = Default(id=1)

        session = create_session()
        session.add(d1)
        session.flush()
        d1.col1 = 'set'
        session.flush()

        eq_(d1.col1, 'set')
        eq_(d1.col2, 'ins')
        eq_(d1.col3, 'up')
        eq_(d1.col4, 'up')


class ExcludedDefaultsTest(fixtures.MappedTest):
    @classmethod
    def define_tables(cls, metadata):
        dt = Table('dt', metadata,
                   Column('id', Integer, primary_key=True,
                          test_needs_autoincrement=True),
                   Column('col1', String(20), default="hello"))

    def test_exclude(self):
        dt = self.tables.dt

        class Foo(fixtures.BasicEntity):
            pass
        mapper(Foo, dt, exclude_properties=('col1',))

        f1 = Foo()
        sess = create_session()
        sess.add(f1)
        sess.flush()
        eq_(dt.select().execute().fetchall(), [(1, "hello")])
