from pathlib import Path
from .doctsformula import DocTsFormula
from .doctsobjecttype import DocTsObjectType
import fetchx.ioutils as ioutils
from .doctemplates import use_template
from .doctstype import DocTsType
from .doctstimeunit import DocTsTimeUnit 


class DocTsDefinition(dict):
    def __init__(self, *args, **kwargs):
        """
        Represents a Time Series Definition in the metamodel.
        Inherits from dict to store definition properties.
        """
        super().__init__(*args, **kwargs)
        self.parent_tsobjecttype = None # type: DocTsObjectType | None
        self.formula = None # type: DocTsFormula | None
        self.predecessors = [] # type: list[DocTsDefinition]
        self.successors = [] # type: list[DocTsDefinition]
        self.tstype = None # type: DocTsType | None
        self.tstimeunit = None # type: DocTsTimeUnit | None
        self._html = None # type: str | None

    @staticmethod
    def load_from_directory(base_path: Path) -> dict[str, 'DocTsDefinition']:
        """
        Load all Time Series Definitions from the specified directory.
        :param base_path: Base path where the metamodel is located.
        :return: A dictionary mapping definition codes to DocTsDefinition instances.
        """
        result = {}
        tsdefinitions_path = base_path / "metamodel/tsDefinition"
        # Load all .json files
        files, directories = ioutils.search_folder(tsdefinitions_path, list_of_extensions=[".json"])
        for file in files:
            value = ioutils.load_json(file) 
            code = Path(file).stem
            result[code] = DocTsDefinition(value)
        return result
    
    @property
    def time_unit(self) -> str:
        """
        Get the time unit associated with this Time Series Definition.
        :return: Time unit as a string.
        """
        result = "--"
        if self.tstimeunit is not None:
            return str(self.tstimeunit)
        if self.tstype is not None and self.tstype.tstimeunit is not None:
            return str(self.tstype.tstimeunit)
        if self.tstype is not None and self.tstype.tsbusinesstype is not None and \
            self.tstype.tsbusinesstype.tstimeunit is not None:
            return str(self.tstype.tsbusinesstype.tstimeunit)
        return '--'
    
    @property
    def value_unit(self) -> str:
        """
        Get the value unit associated with this Time Series Definition.
        :return: Value unit as a string.
        """
        result = "--"
        if self.tstype is not None and self.tstype.tsbusinesstype is not None:
            value_unit = self.tstype.tsbusinesstype.get('valueUnit', '--')
        return result
    
    @property
    def aggregation_function(self) -> str:
        """
        Get the aggregation function associated with this Time Series Definition.
        :return: Aggregation function as a string.
        """
        if self.tstype is not None and self.tstype.tsbusinesstype is not None and \
            'aggregationFunction' in self.tstype.tsbusinesstype:
            result = self.tstype.tsbusinesstype.get('aggregationFunction', '--')
        elif self.tstype is not None and 'aggregationFunction' in self.tstype:
            result = self.tstype.get('aggregationFunction', '--')
        else:
            result = self.get('aggregationFunction', '--')
        return result
    
    @property
    def disaggregation_function(self) -> str:
        """
        Get the disaggregation function associated with this Time Series Definition.
        :return: Disaggregation function as a string.
        """
        if self.tstype is not None and self.tstype.tsbusinesstype is not None and \
            'disaggregationFunction' in self.tstype.tsbusinesstype:
            result = self.tstype.tsbusinesstype.get('disaggregationFunction', '--')
        elif self.tstype is not None and 'disaggregationFunction' in self.tstype:
            result = self.tstype.get('disaggregationFunction', '--')
        else:
            result = self.get('disaggregationFunction', '--')
        return result
    
    @property
    def data_provider(self) -> str:
        """
        Get the data provider associated with this Time Series Definition.
        :return: Data provider as a string.
        """
        result = str(self.get('dataProvider')) if self.get('dataProvider') is not None else 'manual input or import from an external system'
        return result

    def to_string(self, language: str = "en") -> str:
        """
        Convert the Time Series Definition to a string representation.
        :param language: Language code for localization.
        :return: String representation of the Time Series Definition.
        """
        if self._html is not None:
            return self._html
        # Generate documentation using templates
        result = ''
        code = self.get('code','unknown_unknown_unknown_unknown')
        code_split = code.split('_')
        while len(code_split) < 4:
            code_split.append('unknown')  # pad with unknown
        # Generate footer content
        footer_content = ""
        footer_content += f'<strong>Time Zone: </strong>{self.get("tsTimeZoneCode", "--")}, '
        footer_content += f'<strong>Time Sample: </strong>{self.time_unit}, '
        footer_content += f'<strong>Agg Func: </strong>{self.aggregation_function}, '
        footer_content += f'<strong>Disagg Func: </strong>{self.disaggregation_function}, '
        footer_content += f'<br/>'
        if self.value_unit is not None and self.value_unit != '--':
            footer_content += f'<strong>Unit: </strong>{self.value_unit}, '
        if self.tstype is not None:
            footer_content += f'<strong>TS Type: </strong>{self.tstype.get("code","--")}, '
        if self.tstype is not None and self.tstype.tsbusinesstype is not None:
            footer_content += f'<strong>TS Business Type: </strong>{self.tstype.tsbusinesstype.get("code","--")}, '
        footer_content += f'<strong>Object Type: </strong>{self.get("tsObjectTypeCode", "--")} '
        # generate tsdefinition for specific language, e.g., 'en'
        result += use_template('Section_Begin',{'language_code':language}, language=language)
        result += use_template('TSDefinition_Begin',{'TSDefinitionCode':self.get('code','--')}, language=language)
        # generate block with border color from parent tsobjecttype
        result += use_template('TSDefinition_Block',{
            'border_color': self.parent_tsobjecttype.color,
            'footer': footer_content,
            'TSDefinitionCode':self.get('code','--')
            }, language=language
        ) # type: ignore
        result += use_template('TSDefinition_BlockSectionStart',{}, language=language)
        # fill in block content
        result += use_template('TSDefinition_BlockContent',{
            'name': self.get('name','--'),
            'short_name': self.get('shortName','--'),
            'description': self.get('desc','--'),
            'dataprovider': self.data_provider,
        }, language=language)
        # add formula differential calculation info if applicable
        if self.formula is not None:
            if self.formula.get('differentialCalculation', False):
                result += use_template('TSDefinition_BlockContentFormulaDifferentialCalculationYes', {}, language=language)
            else:
                result += use_template('TSDefinition_BlockContentFormulaDifferentialCalculationNo', {}, language=language)
        result += use_template('TSDefinition_BlockSectionEnd',{}, language=language)
        # add formula section if formula exists
        if self.formula:
            formula_str = str(self.formula.get('formula',''))
            # escape special characters
            #formula_str = formula_str.replace("\\", "\\\\").replace('\"', '\\\"').replace('\'', '\\\'')
            result += use_template('TSDefinition_Formula',{'formula': formula_str}, language=language, use_br_for_newlines=False)
        # if there is at least one predecessor and one successor then display them in two separate sections
        if len(self.predecessors) > 0 or len(self.successors) > 0:
            # add predecessors
            result += use_template('TSDefinition_PredecessorsStart',{
                'predecessors_count': str(len(self.predecessors))
            }, language=language)
            for predecessor in self.predecessors:
                result += use_template('TSDefinition_Predecessor',{
                    'predecessor': predecessor.get('code',''),
                    'predecessor_link': f"#{predecessor.get('code','')}"
                }, language=language)
            result += use_template('TSDefinition_PredecessorsEnd',{}, language=language)
            # add successors
            result += use_template('TSDefinition_SuccessorsStart',{
                'SuccessorsCount': str(len(self.successors))
            }, language=language)
            for successor in self.successors:
                result += use_template('TSDefinition_Successor',{
                    'successor': successor.get('code',''),
                    'successor_link': f"#{successor.get('code','')}"
                }, language=language)
            result += use_template('TSDefinition_SuccessorsEnd',{}, language=language)
        # add end section
        result += use_template('TSDefinition_End',{}, language=language)
        result += use_template('Section_End',{}, language=language)
        self._html = result + "\n"
        return self._html

