class RecoveryManagement(object):
    def __init__(self, log_path, path='recovery.txt'):
        self.path = path
        self.db = Datasource()
        self.log_parser = LogParser(log_path)
        self.log_writer = LogWriter(log_path)
        self.generator = SearchEngineGenerator()

    def __call__(self, recover_line):
        with open(self.path, "a") as f:
            save_redo_list = []
            n_line, undo_list = self.log_parser.find_checkpoint()
            for log in self.log_parser.forward(n_line=n_line):
                groups = log.groups()
                command_type = groups[0]
                if 'recover' in command_type:
                    break
                else:
                    if len(groups) == 2:
                        if 'start' in groups[1]:
                            undo_list.append(command_type)
                        elif 'commit' in groups[1]:
                            undo_list.remove(command_type)
                            # transaction = self.log_parser.find_transaction(command_type)
                            # for t in transaction:
                            #     self.execute_recovery(command_type, t, 'new', 'redo')
                            save_redo_list.append(command_type)
                        elif 'abort' in groups[1]:
                            undo_list.remove(command_type)
                            # transaction = self.log_parser.find_transaction(command_type)
                            # for t in transaction:
                            #     self.execute_recovery(command_type, t, 'old', 'redo')
                            save_redo_list.append(command_type)
                    elif command_type.startswith('<T'):
                        self.execute_recovery(command_type, groups[1:], 'new',
                                              'redo')

            save_undo_list = undo_list.copy()
            for log in self.log_parser.backward():
                groups = log.groups()
                command_type = groups[0]
                if command_type in undo_list and 'start' in groups[1]:
                    undo_list.remove(command_type)
                    # transaction = self.log_parser.find_transaction(command_type)
                    # for t in reversed(transaction):
                    #     self.execute_recovery(command_type, t, 'old', 'undo')
                    self.log_writer.free_write(f"{command_type} abort")
                elif command_type in undo_list and command_type.startswith(
                        '<T'):
                    self.execute_recovery(command_type, groups[1:], 'old',
                                          'undo')

            self.log_writer.recover(recover_line)
            self.log_writer.checkpoint()
            f.write(f"recover {recover_line + 1}\n")
            f.write(f"redo {', '.join(save_redo_list)}\n")
            f.write(f"undo {', '.join(save_undo_list)}\n")

    def execute_recovery(self, t_id, t, value_type, do_type):
        '''
        t (tuple): 트랜잭션 튜플
        value_type (enum): old value인지 new value인지
            ['old', 'new']
        do_Type (enum): undo or redo
            ['undo', 'redo']
        '''
        assert value_type in ['old', 'new'], "Value 타입이 잘못되었습니다."
        assert do_type in ['undo', 'redo'], "Do 타입이 잘못되었습니다."

        is_undo = True if do_type == 'undo' else False

        # Update
        if len(t) == 6:
            table, key_field, key, target_field, old_value, new_value = t
            # value = new_value if value_type in 'new' else old_value
            value = new_value if do_type == 'redo' else old_value

            if is_undo:
                self.log_writer.free_write(
                    f"{t_id}, <{table}>.<id:{key}>.<{target_field}>, <{new_value}>, <{old_value}>"
                )
            else:
                self.log_writer.free_write(f"#redo {t_id}_{t}")
            self.db.update_table(table, key_field, key, target_field, value)
        # Delete
        elif len(t) == 5:
            table, key_field, key, old_tuple, _ = t
            if isinstance(old_tuple, str):
                if old_tuple != 'None':
                    old_tuple = old_tuple[1:-1]
                    if old_tuple.endswith(","):
                        old_tuple = old_tuple[:-1]

            if value_type == 'new':
                if is_undo:
                    self.log_writer.free_write(
                        f"{t_id}, <{table}>.<{key_field}:{key}>, <{old_tuple}>, <None>"
                    )
                else:
                    self.log_writer.free_write(f"#redo {t_id}_{t}")
                self.db.delete_table(table, key_field, key)
            else:
                if old_tuple != 'None':
                    if is_undo:
                        self.log_writer.free_write(
                            f"{t_id}, <{table}>.<{key_field}:{key}>, <None>, <{old_tuple}>"
                        )
                    else:
                        self.log_writer.free_write(f"#redo {t_id}_{t}")
                    self.db.insert_table(table, old_tuple)