import asyncio

from windyquery import DB

loop = asyncio.get_event_loop()


def test_0create_table(db: DB):
    async def create_fn():
        await db.schema('TABLE IF NOT EXISTS test_table').create(
            'num1 smallint CONSTRAINT num1nullable NULL CONSTRAINT greaterzero CHECK (num3 > 0)',
            # 'num2 integer NOT NULL DEFAULT nextval(\'distributors_serial\')',
            'num3 bigint NOT NULL CHECK (num3 > 0) DEFAULT 8',
            'num4 decimal(8, 2)',
            'num5 numeric(10, 1)',
            'num6 real',
            'num7 double precision',
            'num8 serial PRIMARY KEY',
            'num9 bigserial',
            'num10 integer',
            'num11 integer GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10)',
            'num12 integer not null',
            'sale money',
            'text1 character varying(5) DEFAULT \'Luso Films\'',
            'text2 varchar(6) UNIQUE',
            'text3 character(7)',
            'text4 char(8)',
            'text5 text',
            'token bytea',
            'time1 timestamp DEFAULT current_timestamp',
            'time2 timestamp without time zone',
            'time3 timestamp with time zone',
            'time4 timestamptz DEFAULT NOW()',
            'time5 date',
            'time6 time',
            'time7 time without time zone',
            'time8 time with time zone',
            'time9 timetz',
            'time10 interval',
            'time11 interval day',
            'time12 interval hour to second',
            'isGood boolean',
            'geo1 point',
            'geo2 line',
            '"my geo3" lseg',
            'geo4 box',
            'geo5 path',
            'geo6 pOLYGON',
            'geo7 circle',
            'net1 cidr',
            'net2 inet',
            'net3 macaddr',
            'bit1 bit(3)',
            'bit2 bit varying(3)',
            'query1 tsvector',
            'query2 tsquery',
            'other1 uuid',
            'other2 xml',
            'array1 timestamp[]',
            'array2 text[3][][1]',
            'array3 uuid[][2]',
            'array4 integer array',
            'array5 line array[2]',
            'json1 json',
            'json2 jsonb',
            'CONSTRAINT sixeight check(num6 > 6 and num8 > 8)',
            'UNIQUE(text1, text2, text3)',
            'UNIQUE(num6, num9) WITH (fillfactor=70)',
            'UNIQUE(num1, num6) INCLUDE (text1, text2)',
            'UNIQUE(other1) USING INDEX TABLESPACE pg_default',
            # 'PRIMARY KEY(num9, sale)',
            'card_id integer CONSTRAINT card_frg_key references cards(id) ON UPDATE NO ACTION',
            'board_id integer references boards (id) ON DELETE CASCADE',
            'FOREIGN KEY (num10) REFERENCES users (id) ON DELETE SET DEFAULT',
        )
    loop.run_until_complete(create_fn())
    rows = loop.run_until_complete(db.table('information_schema.columns').where(
        'table_name', 'test_table').select().order_by('ordinal_position'))
    assert len(rows) == 56
    assert rows[0]['column_name'] == 'num1'
    assert rows[11]['column_name'] == 'sale'
    assert rows[18]['column_name'] == 'time1'
    rows = loop.run_until_complete(db.table('information_schema.table_constraints').where(
        'table_name', 'test_table').select())
    assert len(rows) == 17
    assert rows[0]['constraint_name'] == 'greaterzero'
    assert rows[2]['constraint_name'] == 'sixeight'


def test_1create_table(db: DB):
    async def create_fn():
        # create table like
        await db.schema('TABLE test_users').create(
            'like users'
        )
    loop.run_until_complete(create_fn())
    rows = loop.run_until_complete(db.table('information_schema.columns').where(
        'table_name', 'test_users').select().order_by('ordinal_position'))
    assert len(rows) == 4
    assert rows[1]['column_name'] == 'email'
    assert rows[2]['column_name'] == 'password'
    assert rows[3]['column_name'] == 'admin'
    rows = loop.run_until_complete(db.table('information_schema.table_constraints').where(
        'table_name', 'test_users').select())
    assert len(rows) == 1


def test_2rename(db: DB):
    async def rename_fn():
        # alter table rename
        await db.schema('TABLE IF EXISTS test_users').alter(
            'RENAME TO test_users2'
        )
        # alter table column name
        await db.schema('TABLE IF EXISTS test_users2').alter(
            'RENAME COLUMN admin TO is_admin'
        )
        # alter table column name
        await db.schema('TABLE IF EXISTS ONLY test_users2').alter(
            'RENAME email TO email_address'
        )
        # alter table rename constraint
        await db.schema('TABLE IF EXISTS test_table').alter(
            'RENAME CONSTRAINT sixeight TO sixeight_num',
        )
    loop.run_until_complete(rename_fn())
    rows = loop.run_until_complete(db.table('information_schema.columns').where(
        'table_name', 'test_users2').select().order_by('ordinal_position'))
    assert len(rows) == 4
    assert rows[1]['column_name'] == 'email_address'
    assert rows[2]['column_name'] == 'password'
    assert rows[3]['column_name'] == 'is_admin'
    rows = loop.run_until_complete(db.table('information_schema.table_constraints').where(
        'table_name', 'test_table').where('constraint_name', 'sixeight_num').select())
    assert len(rows) == 1


def test_3modify(db: DB):
    async def modify_fn():
        # alter table set schema
        await db.schema('TABLE IF EXISTS test_users2').alter(
            'SET SCHEMA test1',
        )
        # alter table add column
        await db.schema('TABLE IF EXISTS test1.test_users2').alter(
            'ADD user_token integer GENERATED BY DEFAULT AS IDENTITY (INCREMENT BY 2)',
        )
        # alter table add column 2
        await db.schema('TABLE IF EXISTS test1.test_users2').alter(
            'ADD COLUMN IF NOT EXISTS user_token2 integer GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10)',
        )
    loop.run_until_complete(modify_fn())
    rows = loop.run_until_complete(db.table('information_schema.columns').where(
        'table_name', 'test_users2').select().order_by('ordinal_position'))
    assert len(rows) == 6
    assert rows[1]['column_name'] == 'email_address'
    assert rows[1]['table_schema'] == 'test1'
    assert rows[2]['column_name'] == 'password'
    assert rows[3]['column_name'] == 'is_admin'
    assert rows[4]['column_name'] == 'user_token'
    assert rows[5]['column_name'] == 'user_token2'
    rows = loop.run_until_complete(db.table('information_schema.table_constraints').where(
        'table_name', 'test_table').where('constraint_name', 'sixeight_num').select())
    assert len(rows) == 1


def test_4drop_column(db: DB):
    async def drop_fun():
        # alter table drop column
        await db.schema('TABLE IF EXISTS test1.test_users2').alter(
            'drop user_token2',
        )
        # alter table drop column 2
        await db.schema('TABLE IF EXISTS test1.test_users2').alter(
            'drop COLUMN IF EXISTS password RESTRICT',
        )
    loop.run_until_complete(drop_fun())
    rows = loop.run_until_complete(db.table('information_schema.columns').where(
        'table_name', 'test_users2').select().order_by('ordinal_position'))
    assert len(rows) == 4
    assert rows[1]['column_name'] == 'email_address'
    assert rows[2]['column_name'] == 'is_admin'
    assert rows[3]['column_name'] == 'user_token'


def test_5create_index(db: DB):
    async def index_fun():
        # create index
        await db.schema('INDEX idx_users_email ON test1.test_users2').create(
            'email_address',
            'user_token',
        )
        # create partial index
        await db.schema('UNIQUE INDEX unique_num912 ON test_table').create(
            'num9',
            'num12',
        ).where('num9', '>', 1)
        # create gin index
        await db.schema('INDEX IF NOT EXISTS idx_json2 ON test_table USING GIN').create(
            '(json2->firstname)',
        )
    loop.run_until_complete(index_fun())
    rows = loop.run_until_complete(db.table('pg_indexes').where(
        'tablename', 'test_users2').where('indexname', 'idx_users_email').select())
    assert len(rows) == 1
    rows = loop.run_until_complete(db.table('pg_indexes').where(
        'tablename', 'test_table').where('indexname', 'unique_num912').select())
    assert len(rows) == 1
    assert 'WHERE (num9 > 1)' in rows[0]['indexdef']
    rows = loop.run_until_complete(db.table('pg_indexes').where(
        'tablename', 'test_table').where('indexname', 'idx_json2').select())
    assert len(rows) == 1


def test_6alter_table(db: DB):
    async def alter_fn():
        # alter table alter column type
        await db.schema('TABLE test_table').alter(
            'alter num1 TYPE bigint',
            'alter COLUMN num10 SET DATA TYPE bigint',
            'alter text5 TYPE integer using text5::integer',
            'alter text4 SET DEFAULT \'abc\'',
            'alter COLUMN num3 DROP DEFAULT',
            'alter "my geo3" SET NOT NULL',
            'alter num3 DROP NOT NULL',
            'alter column num12 ADD GENERATED ALWAYS AS IDENTITY (INCREMENT BY 2)',
            'alter num11 SET GENERATED ALWAYS',
            'alter num11 SET START WITH 6',
            'alter num11 SET INCREMENT BY 3',
            'alter num11 RESTART WITH 9',
            'alter COLUMN num11 SET GENERATED BY DEFAULT SET START WITH 2 SET INCREMENT BY 8 RESTART WITH 10',
            'alter num12 drop identity if exists',
            'alter num1 SET STATISTICS 100',
            'alter num1 SET STATISTICS -1',
            'alter text1 set storage plain',
            'add CONSTRAINT num7constr check(num7 > 6 and num7 > 8)',
            'add UNIQUE(text1, text2) WITH (fillfactor=70)',
            'add FOREIGN KEY (num12) REFERENCES cards (id) ON DELETE SET NULL',
            'drop CONSTRAINT IF EXISTS sixeight CASCADE ',
        )
    loop.run_until_complete(alter_fn())
    rows = loop.run_until_complete(db.table('information_schema.columns').where(
        'table_name', 'test_table').select().order_by('ordinal_position'))
    assert len(rows) == 56
    assert rows[0]['data_type'] == 'bigint'
    assert rows[16]['data_type'] == 'integer'
    rows = loop.run_until_complete(db.table('information_schema.table_constraints').where(
        'table_name', 'test_table').select())
    assert len(rows) == 20


def test_7drop(db: DB):
    async def drop_fn():
        # drop index
        await db.schema('INDEX CONCURRENTLY test1.idx_users_email').drop()
        # drop index 2
        await db.schema('INDEX IF EXISTS test1.idx_users_email, idx_json2').drop('CASCADE')
        # drop table
        await db.schema('TABLE test_table').drop()
        # drop table if exists
        await db.schema('TABLE IF EXISTS test_table, test_table2, test_table3, test1.test_users2').drop(
            'CASCADE'
        )
    loop.run_until_complete(drop_fn())
    rows = loop.run_until_complete(db.table('pg_indexes').where(
        'tablename', 'test_users2').where('indexname', 'idx_users_email').select())
    assert len(rows) == 0
    rows = loop.run_until_complete(db.table('pg_indexes').where(
        'tablename', 'test_table').where('indexname', 'idx_json2').select())
    assert len(rows) == 0
    rows = loop.run_until_complete(db.table('information_schema.tables').where(
        'table_name', 'test_users2').select())
    assert len(rows) == 0
    rows = loop.run_until_complete(db.table('information_schema.tables').where(
        'table_name', 'test_table').select())
    assert len(rows) == 0
