import re
import numpy as np

from sdsvkie.utils.post_processing import near
from sdsvkie.utils.post_processing.common_post_processing import normalize_number_wordgroup, tax_code_processing
from sdsvkie.utils.post_processing.post_processing_datetime import post_processing_datetime
from sdsvkie.utils import merge_boxes, Word_group, merge_wordgroups, distance_of_boxes, y_distance
from sdsvkie.utils.post_processing.receipt_post_processing import is_wg_same_line


__all__ = ["POSTPROCESS_FUNC"]


class CommonPostprocessing:
    r"""Functions to postprocess fields

    Notes:
    * I/O formats for text postprocessing functions must be the same
    * I/O formats for box postprocessing functions must be the same
    """
    re_number_pattern = '[0-9]'
    re_not_number_pattern = '[^0-9]'
    re_not_staff_code = '[^a-zA-Z0-9]'
    re_char_pattern = '[a-zA-Z]'
    re_not_char_pattern = '[^a-zA-Z]'
    re_not_phone_pattern = '[^0-9+]'
    re_not_tax_pattern = '[^0-9-]'
    re_remove_extra_space = '\s+'

    
    def remove_numbers(text, metadata):
        result = re.sub(CommonPostprocessing.re_not_name_pattern, "", text) 

        return result

    def remove_nan(text, metadata):
        r"""Get number characters only"""
        result = re.sub(CommonPostprocessing.re_not_number_pattern, "", text) 

        return result
    

    def remove_not_phone(text, metadata):
        r"""Get phone number characters only"""
        result = re.sub(CommonPostprocessing.re_not_phone_pattern, "", text) 

        return result
    
    def remove_not_tax(text, metadata):
        r"""Get tax number characters only"""
        # print("before: ", text)
        # text = tax_code_processing(text)
        # print("after: ", text)
        res = re.sub('[^0-9- ]', "", text)
        res = res.strip()
        num_spaces = res.count(" ")

        # duplicate with 14 chars tax
        print(text)
        if "-" in res and len(res) - num_spaces > 14:
            tmp_res = res
            for idx in range(len(res)):
                if res[idx] == " ":
                    if res[idx-1] == res[idx+1]:
                        tmp_res = res[:idx] + res[idx+2:]
                if len(re.sub('[^0-9-]', "", tmp_res)) == 14:
                    break
            res = tmp_res
        elif len(res) - num_spaces > 10:
            tmp_res = res
            for idx in range(len(res)):
                if res[idx] == " ":
                    if res[idx-1] == res[idx+1]:
                        tmp_res = res[:idx] + res[idx+2:]
                if len(re.sub('[^0-9-]', "", tmp_res)) == 10:
                    break
            res = tmp_res

        result = re.sub(CommonPostprocessing.re_not_tax_pattern, "", res)
        
        print("--> ", text)
        return result
    
    def remove_duplicate_space(text, metadata):
        result = re.sub(CommonPostprocessing.re_remove_extra_space, " ", text)
        return result


class InvoicePostprocessing:
    """Function to postprocess VAT invoice
    """
    @staticmethod
    def process_tax_amount(text, metadata=None):
        print("tax before: ", text)
        text = CommonPostprocessing.remove_nan(text, metadata)
        text = "0" if len(text) == 0 else text
        print("tax after: ", text)
        return text 

    @staticmethod
    def process_no_wordgroup(items, metadata=None):
        if len(items) <= 1:
            return items
        if metadata is not None:
            wg_res = metadata.get("wg_res", None)
        no_key_wg = wg_res.get("no_key", None)

        if no_key_wg and len(no_key_wg) > 0:
            no_key_box = no_key_wg[0].boundingbox
            height_no_key = no_key_box[3] - no_key_box[1] if len(no_key_box) == 4 else 10000
            items = [no_wg for no_wg in items if y_distance(no_wg.boundingbox, no_key_box) <= 3*height_no_key]

        return items
    
    @staticmethod
    def process_serial_wordgroup(items, metadata=None):
        if len(items) <= 1:
            return items
        if metadata is not None:
            wg_res = metadata.get("wg_res", None)
        serial_key_wg = wg_res.get("serial_key", None)

        if serial_key_wg and len(serial_key_wg) > 0:
            serial_key_box = serial_key_wg[0].boundingbox
        
        
        height_serial_key = serial_key_box[3] - serial_key_box[1] if len(serial_key_box) == 4 else 10000
        items = [serial_wg for serial_wg in items if y_distance(serial_wg.boundingbox, serial_key_box) <= 2*height_serial_key]

        return items
        

    @staticmethod
    def process_date_wordgroup(items, metadata=None):
        
        def _not_too_far_from_wg(curr_wg: Word_group, next_wg: Word_group, thresh=3):
            height_curr_wg = curr_wg.boundingbox[3] - curr_wg.boundingbox[1]
            _distance = y_distance(curr_wg.boundingbox, next_wg.boundingbox)
            return True if _distance <= height_curr_wg * thresh else False
            
        

        # sort by y-axis
        date_wordgroups = sorted(items, key=lambda date_wg: date_wg.boundingbox[1]) 
        line = [date_wordgroups[0]]
        for idx, wg in enumerate(date_wordgroups[1:]):
            if is_wg_same_line(line[-1], wg) or _not_too_far_from_wg(curr_wg=line[-1], next_wg=wg):
                line.append(wg)
        
        
        # sort by x-axis
        date_line = sorted(line, key=lambda wg: wg.boundingbox[0])   
        
        date_wg = merge_wordgroups(date_line)
        return date_wg
    
    @staticmethod 
    def process_tax_wordgroup(items, metadata=None):
        """
        params:
            items:
            metadata (dict): ['anchor_box']
        """

        # case 1: multiple taxes 
        if metadata is not None:
            field_name = metadata.get("field_name", None)
            wg_res = metadata.get("wg_res", None)

         # sort by y-axis
        tax_wordgroups = sorted(items, key=lambda tax_wg: tax_wg.boundingbox[1])
        if len(tax_wordgroups) > 1:
            anchor = None
            # get coordinate of buyer/seller name
            if field_name is not None and wg_res is not None:
                if field_name == "seller_tax_code_value":
                    name_wg_list = wg_res.get("seller_company_name_value", None)
                elif field_name == "buyer_tax_code_value":
                    name_wg_list = wg_res.get("buyer_company_name_value", None)
                if name_wg_list is not None and len(name_wg_list) > 0:
                    name_wg = name_wg_list[0]
                    anchor = name_wg.boundingbox
            if anchor:
                tax_wordgroups = [wg for wg in tax_wordgroups if wg.boundingbox[1] >= anchor[1]]

        line = [tax_wordgroups[0]]
        for idx, wg in enumerate(tax_wordgroups[1:]):
            if is_wg_same_line(line[-1], wg):
                line.append(wg)
        # sort by x-axis
        tax_line = sorted(line, key=lambda wg: wg.boundingbox[0])   


        tax_wg = merge_wordgroups(tax_line)
        return tax_wg
    
    @staticmethod
    def process_date(item, metadata=None):
        """

        Args:
            item (str): date string
            metadata (dict, optional): metadata dict. Defaults to None.
        """
        item = post_processing_datetime(item)
        return item 
    @staticmethod
    def process_total_value_in_word(item, metadata=None):
        re_remove_special_char_pattern = '[^aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪửỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ0123456789, ]'
        item = re.sub(re_remove_special_char_pattern, " ", item)
        item = CommonPostprocessing.remove_duplicate_space(item, metadata)
        return item
    
    @staticmethod
    def process_address(item, metadata=None):
        item = CommonPostprocessing.remove_duplicate_space(item, metadata)
        return item
    @staticmethod
    def process_company_name(item, metadata=None):
        item = CommonPostprocessing.remove_duplicate_space(item, metadata)
        return item
    

POSTPROCESS_FUNC = {
    "invoice_postprocess": {
        "no_value": {
            "wordgroup": InvoicePostprocessing.process_no_wordgroup
        },
        # "form_value": {
        #     "text": CommonPostprocessing.remove_numbers
        # },
        "serial_value": {
            "wordgroup": InvoicePostprocessing.process_serial_wordgroup
        },
        "date_value": {
            "wordgroup": InvoicePostprocessing.process_date_wordgroup,
            "text": InvoicePostprocessing.process_date,
        },
        "buyer_tax_code_value": {
            "wordgroup": InvoicePostprocessing.process_tax_wordgroup,
            "text": CommonPostprocessing.remove_not_tax
        },
        "buyer_tel_value": {
            "text": CommonPostprocessing.remove_not_phone
        },
        "buyer_company_name_value": {
            "text": InvoicePostprocessing.process_company_name
        },
        "buyer_address_value": {
            "text": InvoicePostprocessing.process_address
        },
        
        "seller_tax_code_value": {
            "wordgroup": InvoicePostprocessing.process_tax_wordgroup,
            "text": CommonPostprocessing.remove_not_tax
        },
        "seller_tel_value": {
            "text": CommonPostprocessing.remove_not_phone
        },
        "seller_company_name_value": {
            "text": InvoicePostprocessing.process_company_name
        },
        "seller_address_value": {
            "text": InvoicePostprocessing.process_address
        },
        "tax_amount_value": {
            "text": InvoicePostprocessing.process_tax_amount
        },
        "total_value": {
            "text": CommonPostprocessing.remove_nan
        },
        "total_in_words_value": {
            "text": InvoicePostprocessing.process_total_value_in_word
        },
        
    },
    "receipt_postprocess": {
    }
    
}