import sqlite3
import os, logging
from saffron.settings import lamden_db_file
from contextlib import suppress
import pickle


create_accounts = 'CREATE TABLE accounts (name text primary key, address text)'

create_contracts = '''
                CREATE TABLE contracts (
                name text primary key,
                address text,
                deployed boolean,
                abi text,
                metadata text,
                gas_estimates blob,
                method_identifiers blob,
                instance blob
                )'''
select_from = 'SELECT * FROM {table} WHERE {name} {address}'.format
log = logging.getLogger(__file__)
print(lamden_db_file)
connection = sqlite3.connect(lamden_db_file)
cursor = connection.cursor()

def init_dbs(sqls):
    # graceful initialization tries to create new tables as a test to see if this is a new DB or not
    for s in sqls:
        with suppress(sqlite3.OperationalError):
            cursor.execute(s)
init_dbs([create_contracts, create_accounts])

def exec_sql(sql):
    try:
        response = cursor.execute(sql)
    except Exception as e:
        return None;
    return response

def name_or_address(name, address):
    name = ' name = "{}"'.format(name) if name else None
    address = ' address = "{}"'.format(address) if address else None
    assert name != None or address != None
    return name, address

def contract_exists(name=None, address=None, table='contracts'):
    _name, _address = name_or_address(name, address)
    try:
        # XXX: is this a security risk if users are able to submit "name" or "address"
        # XXX: see ? syntax for sql queries for proper escaping
        return next(exec_sql(select_from(table=table, name=_name, address=_address)))
    except StopIteration:
        return None, None
    except Exception as e:
        return None, None

def account_exists(name=None, address=None, table='accounts'):
    _name, _address = name_or_address(name, address)
    try:
        return next(exec_sql(select_from(table=table, name=_name, address=_address)))
    except StopIteration:
        return None, None
    except Exception as e:
        return None, None

def init_account(name=None, address=None, table='accounts'):
    try:
        return Account(name=name, address=address)
    except Exception as e:
        import traceback
        t = traceback.format_exc()
        import pdb;pdb.set_trace()
        return ValueError('Unable to initialize Account with values: '
                          '{name} {address}'.format(name=name, address=address))

def insert_account(name, address):
    assert name, address
    try:
        cursor.execute('INSERT INTO accounts(name, address) VALUES (?, ?)', (name, address))
        connection.commit()
    except sqlite3.IntegrityError as e:
        return 'Account exists'

def update_contract(address, instance, name):
    assert address
    assert instance
    assert name
    result = cursor.execute(update_contracts_sql, (address, pickle.dumps(instance), name))
    return [x for x in cursor.execute('select * from contracts where address=?', (address, ))]

def insert_contract(name: str, abi, bytecode: str, gas_estimates, method_identifiers, cwd):
    '''insert_contract into the localdb, also converts the type
    '''
    assert name
    assert abi
    assert bytecode
    assert gas_estimates
    assert method_identifiers
    gas = pickle.dumps(gas_estimates)
    methods = pickle.dumps(method_identifiers)
    result = cursor.execute(insert_contract_sql, (name,
                                                str(abi),
                                                bytecode,
                                                sqlite3.Binary(gas),
                                                sqlite3.Binary(methods)))
    connection.commit()
    return result


insert_contract_sql = '''
                         INSERT INTO contracts (
                         name,
                         abi,
                         metadata,
                         gas_estimates,
                         method_identifiers) VALUES (?,?,?,?,?)'''

update_contracts_sql = '''
                         UPDATE contracts
                         SET
                         address = ?,
                         instance = ?,
                         deployed = 'true'
                         where name = ? ;'''

input_json = '''{"language": "Solidity","sources": {
                                 "{{name}}": {
                                         "content": {{sol}}
                                 }
                         },
                         "settings": {
                                 "outputSelection": {
                                         "*": {
                                                 "*": [ "metadata", "evm.bytecode", "abi", "evm.bytecode.opcodes", "evm.gasEstimates", "evm.methodIdentifiers" ]
                                         }
                                 }
                         }
               }'''