from typing import TypeVar, List, Union
from copy import deepcopy

from sqlalchemy import or_
from sqlalchemy.orm import declarative_base
from sqlalchemy.sql.elements import BinaryExpression
from pydantic import BaseModel

{{ import }}

Base = TypeVar("Base", bound=declarative_base)


def find_query_builder(param: dict, model: Base) -> List[Union[BinaryExpression]]:
    query = []
    for column_name, value in param.items():
        if ExtraFieldType.Comparison_operator in column_name or ExtraFieldType.Matching_pattern in column_name:
            continue
        if ExtraFieldTypePrefix.List in column_name:
            type_ = ExtraFieldTypePrefix.List
        elif ExtraFieldTypePrefix.From in column_name:
            type_ = ExtraFieldTypePrefix.From
        elif ExtraFieldTypePrefix.To in column_name:
            type_ = ExtraFieldTypePrefix.To
        elif ExtraFieldTypePrefix.Str in column_name:
            type_ = ExtraFieldTypePrefix.Str
        else:
            query.append((getattr(model, column_name) == value))
            # raise Exception('known error')
            continue
        sub_query = []
        table_column_name = column_name.replace(type_, "")
        operator_column_name = column_name + process_type_map[type_]
        operators = param.get(operator_column_name, None)
        if not operators:
            raise QueryOperatorNotFound(400, f'The query operator of {column_name} not found!')
        if not isinstance(operators, list):
            operators = [operators]
        for operator in operators:
            sub_query.append(process_map[operator](getattr(model, table_column_name), value))
        query.append((or_(*sub_query)))
    return query


def value_of_list_to_str(request_or_response_object, columns):
    received_request = deepcopy(request_or_response_object.__dict__)
    if isinstance(columns, str):
        columns = [columns]
    if 'insert' in request_or_response_object.__dict__:
        insert_str_list = []
        for insert_item in request_or_response_object.__dict__['insert']:
            for column in columns:
                for insert_item_column, _ in insert_item.__dict__.items():
                    if column in insert_item_column:
                        value_ = insert_item.__dict__[insert_item_column]
                        if value_ is not None:
                            if isinstance(value_, list):
                                str_value_ = [str(i) for i in value_]
                            else:
                                str_value_ = str(value_)
                            setattr(insert_item, insert_item_column, str_value_)
            insert_str_list.append(insert_item)
        setattr(request_or_response_object, 'insert', insert_str_list)
    else:
        for column in columns:
            for received_column_name, _ in received_request.items():
                if column in received_column_name:
                    value_ = received_request[received_column_name]
                    if value_ is not None:
                        if isinstance(value_, list):
                            str_value_ = [str(i) for i in value_]
                        else:
                            str_value_ = str(value_)
                        setattr(request_or_response_object, received_column_name, str_value_)


def filter_none(request_or_response_object):
    received_request = deepcopy(request_or_response_object.__dict__)
    if 'insert' in received_request:
        insert_item_without_null = []
        for received_insert in received_request['insert']:
            received_insert_ = deepcopy(received_insert)
            for received_insert_item, received_insert_value in received_insert_.__dict__.items():
                if hasattr(received_insert_value, '__module__'):
                    if received_insert_value.__module__ == 'fastapi.params' or received_insert_value is None:
                        delattr(received_insert, received_insert_item)
                elif received_insert_value is None:
                    delattr(received_insert, received_insert_item)

            insert_item_without_null.append(received_insert)
        setattr(request_or_response_object, 'insert', insert_item_without_null)
    else:
        for name, value in received_request.items():
            if hasattr(value, '__module__'):
                if value.__module__ == 'fastapi.params' or value is None:
                    delattr(request_or_response_object, name)
            elif value is None:
                delattr(request_or_response_object, name)

class ExcludeUnsetBaseModel(BaseModel):
    def dict(self, *args, **kwargs):
        if kwargs and kwargs.get("exclude_none") is not None:
            kwargs["exclude_unset"] = True
            return BaseModel.dict(self, *args, **kwargs)

def clean_input_fields(param: Union[dict, list], model: Base):
    assert isinstance(param, dict) or isinstance(param, list) or isinstance(param, set)

    if isinstance(param, dict):
        stmt = {}
        for column_name, value in param.items():
            if '_initialised__' in column_name:
                continue
            column = getattr(model, column_name)
            actual_column_name = column.expression.key
            stmt[actual_column_name] = value
        return stmt
    if isinstance(param, list) or isinstance(param, set):
        stmt = []
        for column_name in param:
            if not hasattr(model, column_name):
                raise UnknownColumn(400,f'Column {column_name} is not existed')
            column = getattr(model, column_name)
            actual_column_name = column.expression.key
            stmt.append(actual_column_name)
        return stmt