import inspect
from typing import Type, Dict, Any, Optional

from .clickhouse_types import ClickHouseType, PartitionBy
from ..utils.representable import Representable
from .abstract.selectable import Selectable


class NoDefaultValue:
    pass


class Column(Representable, Selectable):
    column_id = 0

    def __init__(self,
                 column_type: Type[ClickHouseType],
                 *,
                 primary_key: bool = False,
                 partition_by: PartitionBy = PartitionBy.disabled(),
                 partition_args: Dict[str, Any] = None,
                 default: Any = NoDefaultValue,
                 default_sql: Optional[str] = None,
                 nullable: Optional[bool] = False,
                 ):
        super(Column, self).__init__(column_type, nullable)
        if partition_args is None:
            partition_args = {}
        self.partition_by = partition_by
        self.partition_args = partition_args
        # self.column_type = column_type
        self.primary_key = primary_key
        self.default = default
        self.default_sql = default_sql
        if self.default != NoDefaultValue and self.default_sql is not None:
            raise RuntimeError('only one type of default allowed')
        self.column_id = Column.column_id
        # self.name = None
        self.select_key_wrapper = None
        Column.column_id += 1

    def has_default_value(self):
        return self.default != NoDefaultValue

    def has_default_sql_value(self):
        return self.default_sql is not None

    async def get_default_value(self):
        if self.default == NoDefaultValue:
            val = None
        elif inspect.iscoroutinefunction(self.default):
            val = await self.default()
        elif callable(self.default):
            val = self.default()
        else:
            val = self.default
        return val

    def to_partition_key_name(self) -> Optional[str]:
        if self.partition_by:
            return self.partition_by.format(column=self.name, **self.partition_args)
        return None

    def to_column_descriptor(self) -> str:
        type_str = self.clickhouse_type.clickhouse_type
        if self.nullable:
            type_str = f'Nullable({type_str})'
        if self.default_sql is not None:
            type_str = f'{type_str} DEFAULT {self.default_sql}'

        return f"`{self.name}` {type_str}"

    def to_selector(self) -> str:
        selector = self.name
        if self.select_key_wrapper:
            selector = self.select_key_wrapper.format(selector)
        return selector
