from ...logging import logging

from typing import Any, Sequence



class Table:
    __slots__ = ('__columns', '__data')

    @logging(prefix=__name__)
    def __init__(self, table: dict[str,list[str|dict[str,Any]]] = None):
        """
        Create table object from dictionary.

        :param table:
            {
                'columns': ['column[0].name', 'column[1].name', ...],
                'data': [
                    {'column[0].name': 'row[0].column[0].value', 'column[1].name': 'row[0].column[1].value', ...},
                    {'column[0].name': 'row[1].column[0].value', 'column[1].name': 'row[1].column[1].value', ...},
                    ...
                ]
            }
        """

        # default -----
        if table is None:
            table = {
                'columns': [],
                'data': []
            }

        # validate -----
        for ii in ('columns', 'data'):
            if ii not in table:
                raise KeyError(f'Parameter "{ii}" not found')

            if not isinstance(table[ii], list):
                raise TypeError(f'Parameter "{ii}" is not a "list"')

        for i, row in enumerate(table['data']):
            if not isinstance(row, dict):
                raise TypeError(f'Parameter "data"[{i}] is not a "dict"')

            if len(row) != len(table['columns']):
                raise ValueError(f'Parameter "data"[{i}] length not match with "columns" length')

            for column in table['columns']:
                if column not in row:
                    raise Exception(f'Parameter "data"[{i}]["{column}"] not found')

        # init -----
        self.__columns = table['columns']
        self.__data = table['data']
        pass


    @property
    def columns(self) -> list[str]:
        """
            ['column[0].name', 'column[1].name', ...]
        """
        return self.__columns


    @property
    def data(self) -> list[dict[str,Any]]:
        """
            [
                {'column[0].name': 'row[0].column[0].value', 'column[1].name': 'row[0].column[1].value', ...},
                {'column[0].name': 'row[1].column[0].value', 'column[1].name': 'row[1].column[1].value', ...},
                ...
            ]
        """
        return self.__data


    def __repr__(self) -> str:
        return f'Table({self.__columns})'


    def __str__(self) -> str:
        return str({
            'columns': self.__columns,
            'data': self.__data
        })


    @logging(prefix=__name__)
    def filter(
        self,
        include: Sequence[str] = None,
        exclude: Sequence[str] = None
    ):
        """
        Return filtered table.

        :param include: (column[0], column[1], ...) (default=$any)
        :param exclude: (column[0], column[1], ...) (default=$none)
        :return: table
        """

        # default -----
        if include is None:
            include = tuple(self.__columns)

        if exclude is None:
            exclude = ()

        # init -----
        return Table(
            table={
                'columns': [
                    column
                    for column in self.__columns
                    if column in include and column not in exclude
                ],
                'data': [
                    {
                        column: value
                        for column, value in row.items()
                        if column in include and column not in exclude
                    }
                    for row in self.__data
                ]
            },
            logger=self.filter.logger
        )

    pass
