# -*- coding: utf-8 -*-
import io
import traceback
from ppms.constants import *
from ppms import ppms
from ppms.database_integrity import db_consistence_test
from ppms.migration import SQLPacket, PacketCategory, get_packet_instance_from_path


class ConsistencyCheckWorkArounder(io.StringIO):
    """Workaround for the consistency check closing files"""

    def __enter__(self):
        self.original_close = self.close
        self.close = lambda: True

        self.result = """"""

        return self

    def __exit__(self, *args):
        self.original_close()

    def write(self, string):
        # not the best solution but not possible to do differently without
        # changing the relation check
        if "SELECT * FROM" in string.upper():
            self.result += string + "\n"


def find_inconsistencies_in_schemas(schemas=None):
    """Finds inconsistent data in schemas and returns the sql statements to find them,
    already formated for a log entry.
    schemas must be a list with schema names e.g. [Q1B, Q2B]"""
    inconsistencies_sql_searches = ""
    if not schemas:
        schemas = ['Q1B', 'Q2B', 'Q3B', 'Q4B', 'Q5B', 'Q6B']
    relation_consistence_test = db_consistence_test.RelationCheck()

    for schema in schemas:
        with ConsistencyCheckWorkArounder() as dummy_log:
            relation_consistence_test.start(schema, file=dummy_log, write_sql=True)
            if dummy_log.result:
                inconsistencies_sql_searches += "\nTo find the inconsistencies in schema" + schema + ": \n"
                inconsistencies_sql_searches += dummy_log.result + "\n"

    return inconsistencies_sql_searches


class EnableConstraints(SQLPacket):
    """Enables constraints previously added and validates existing data.
    """
    run_on_startup = False
    done_after_success = False
    category = PacketCategory.CATEGORY_HELPER
    wi_number = 14043

    PACKET_PATH = 'migration.after_db_import.DB_39_5_7.WI17120_create_constraints.A_CreateConstraints'

    GET_CONSTRAINTS = {DBMS_MSSQL: """SELECT TABLE_CATALOG, TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES
                                          WHERE TABLE_TYPE = 'BASE TABLE'""",
                       DBMS_ORACLE: """SELECT c.owner, c.table_name, c.constraint_name
                                          FROM user_constraints c, user_tables t
                                          WHERE c.table_name = t.table_name
                                          AND (c.status = 'DISABLED' or c.validated != 'VALIDATED')
                                          ORDER BY c.constraint_type
                                    """}

    SQL_PROCEDURE = {DBMS_MSSQL: ['''ALTER TABLE "{table_name}" CHECK CONSTRAINT ALL''',
                                  '''ALTER TABLE "{table_name}" WITH CHECK CHECK CONSTRAINT ALL'''],
                     DBMS_ORACLE: ["""alter table "{owner}"."{table_name}" enable constraint {constraint_name}""",
                                   """alter table "{owner}"."{table_name}" enable validate constraint {constraint_name}"""]}
    constraint_errors = ""

    def log_sql_error(self, constraint):
        type, value, trace = sys.exc_info()
        error_text = traceback.format_exception_only(type, value)[0].split("\\n", 1)[0]
        self.constraint_errors += '''\n\n''' + \
                                  error_text + '\n' +\
                                  constraint

    def fix(self, runner):
        errors = 0
        for owner, table_name, constraint_name in self.select_and_log(self.GET_CONSTRAINTS[self.DBMS]):
            for statment in self.SQL_PROCEDURE[self.DBMS]:
                try:
                    self.modify_and_log(statment.format(**locals()))
                except:
                    self.log_sql_error(statment.format(**locals()))
                    errors += 1

        if errors:
            self.log("The following errors occurred while creating the constraints: ")
            self.log(self.constraint_errors)
            inconsistencies_sql_searches = find_inconsistencies_in_schemas()
            if inconsistencies_sql_searches:
                self.log(
                    "Not all the constraints could be activated. Check the errors above to figure out the constraints concerned. Correct the data in the affected tables in order to activate the foreign keys .\nYou can find inconsistent data using the following SQL statements. Note that there might also be other problems, so make sure to check all the errors above.")
                self.log(inconsistencies_sql_searches)

            return self.fail("There are still some problems left. Please resolve them and run this packet again.")

        # set the packet state of the create constraint packet to success if this packet succeeded
        uuid = ppms.db_select("select uuid from MIGRATIONRULE where pythonpath='{path}'".format(path=self.PACKET_PATH))[0][0]
        create_constraints_packet = get_packet_instance_from_path(self.PACKET_PATH, uuid=uuid)

        create_constraints_packet.log("Assuming all errors of this migration packet has been resolved, since EnableConstraints run successful. Setting this migration packet to succeeded too.")
        create_constraints_packet.success()
        create_constraints_packet.save_history()
        return self.success()