import logging
from core.processor import Processor
from utils.CodeExplainerUtil import CodeExplainerUtil


class DATA_MASKINGProcessor(Processor):
    TPL: str = '{"source_key":"", "content_clean_func":"return content", "masking_func":"return \'SJB-\' + str(colnum) + str(rownum) ", "masking_column":"0", "masking_dict_name":"", "masking_dict_inverted":"Yes"}'

    DESC: str = '''
        Perform single-column data masking on a collection (list of rows). Each row is a list
        where a specific column is cleaned and then replaced with a masked value generated by
        a custom function. A masking dictionary is maintained to ensure the same original value
        always maps to the same masked value.

        The masking_func has access to: mask_dict, row, rownum, colnum.
        The content_clean_func has access to: content (the raw cell value).

        - source_key: Key in data_chain for the input list of rows to mask (supports expression, default: "")
        - content_clean_func: Python function body to clean/normalize cell content before masking; takes (content) (supports expression, default: "return content")
        - masking_func: Python function body to generate a masked replacement value; takes (mask_dict, row, rownum, colnum) (supports expression, default: "return 'SJB-' + str(colnum) + str(rownum)")
        - masking_column: Zero-based column index to apply masking on (supports expression, default: "0")
        - masking_dict_name: Key in data_chain to store the masking dictionary after processing; if empty, the dict is discarded (supports expression, default: "")
        - masking_dict_inverted: If "Yes" or "yes", stores the inverted mapping (masked -> original) instead of (original -> masked) (supports expression, default: "Yes")

    '''

    def get_category(self) -> str:
        return super().CATE_DATA_PROCESSING

    def process(self):
        masking_dict = {}

        given_collection = self.get_data(self.get_param('source_key'))
        masking_dict_inverted = True if str(self.get_param('masking_dict_inverted')).lower() == "yes" else False
        masking_dict_name = self.get_param('masking_dict_name')
        masking_func_body = self.get_param('masking_func')
        content_clean_func = self.get_param('content_clean_func')
        masking_columnnum = int(self.get_param('masking_column'))

        masking_func = CodeExplainerUtil.create_and_execute_func('DATA_MASKINGProcessor_masking',
                                                                 '(masking_dict, row, rownum, colnum, p)',
                                                                 masking_func_body)

        content_clean_func = CodeExplainerUtil.create_and_execute_func('DATA_MASKINGProcessor_clean',
                                                                 '(content, p)',
                                                                 content_clean_func)

        for rownum, row in enumerate(given_collection):
            masking_column_content = content_clean_func(row[masking_columnnum], self)

            if masking_column_content not in masking_dict:
                masking_dict[masking_column_content] = masking_func(masking_dict, row, rownum, masking_columnnum, self)
                logging.info(f'"{masking_column_content}" -> {str(masking_dict[masking_column_content])}')

            row[masking_columnnum] = masking_dict[masking_column_content]

        if masking_dict_name:
            if masking_dict_inverted:
                masking_dict = {v: k for k, v in masking_dict.items()}
            self.populate_data(masking_dict_name, masking_dict)
        else:
            masking_dict.clear()
