from enum import Enum, auto
from itertools import chain
from sqlalchemy import or_
from strenum import StrEnum


class CrudMethods(Enum):
    FIND_ONE = "FIND_ONE"
    FIND_MANY = "FIND_MANY"
    UPDATE_ONE = "UPDATE_ONE"
    UPDATE_MANY = "UPDATE_MANY"
    PATCH_ONE = "PATCH_ONE"
    PATCH_MANY = "PATCH_MANY"
    CREATE_ONE = "CREATE_ONE"
    CREATE_MANY = "CREATE_MANY"
    DELETE_ONE = "DELETE_ONE"
    DELETE_MANY = "DELETE_MANY"

    @staticmethod
    def get_full_crud_method():
        return [CrudMethods.FIND_MANY,
                CrudMethods.FIND_ONE,
                CrudMethods.CREATE_MANY,
                CrudMethods.PATCH_ONE,
                CrudMethods.PATCH_MANY,
                CrudMethods.PATCH_ONE,
                CrudMethods.UPDATE_MANY,
                CrudMethods.UPDATE_ONE,
                CrudMethods.DELETE_MANY,
                CrudMethods.DELETE_ONE]



class ExtraFieldTypePrefix(StrEnum):
    List = '____list'
    From = '____from'
    To = '____to'
    Str = '____str'



class ExtraFieldType(StrEnum):
    Comparison_operator = '_____comparison_operator'
    Matching_pattern = '_____matching_pattern'



class MatchingPatternInStringBase(StrEnum):
    case_insensitive = auto()
    case_sensitive = auto()
    not_case_insensitive = auto()
    not_case_sensitive = auto()
    contains = auto()


class PGSQLMatchingPattern(StrEnum):
    match_regex_with_case_sensitive = auto()
    match_regex_with_case_insensitive = auto()
    does_not_match_regex_with_case_sensitive = auto()
    does_not_match_regex_with_case_insensitive = auto()
    similar_to = auto()
    not_similar_to = auto()


PGSQLMatchingPatternInString = StrEnum('PGSQLMatchingPatternInString',
                                       {Pattern: auto() for Pattern in
                                        chain(MatchingPatternInStringBase, PGSQLMatchingPattern)})

process_type_map = {
    ExtraFieldTypePrefix.List: ExtraFieldType.Comparison_operator,
    ExtraFieldTypePrefix.From: ExtraFieldType.Comparison_operator,
    ExtraFieldTypePrefix.To: ExtraFieldType.Comparison_operator,
    ExtraFieldTypePrefix.Str: ExtraFieldType.Matching_pattern,
}

class RangeFromComparisonOperators(StrEnum):
    Greater_than = auto()
    Greater_than_or_equal_to = auto()


class RangeToComparisonOperators(StrEnum):
    Less_than = auto()
    Less_than_or_equal_to = auto()


class ItemComparisonOperators(StrEnum):
    Equal = auto()
    Not_equal = auto()
    In = auto()
    Not_in = auto()


process_map = {
    RangeFromComparisonOperators.Greater_than:
        lambda field, value: field > value,

    RangeFromComparisonOperators.Greater_than_or_equal_to:
        lambda field, value: field >= value,

    RangeToComparisonOperators.Less_than:
        lambda field, value: field < value,

    RangeToComparisonOperators.Less_than_or_equal_to:
        lambda field, value: field <= value,

    ItemComparisonOperators.Equal:
        lambda field, values: or_(field == value for value in values),

    ItemComparisonOperators.Not_equal:
        lambda field, values: or_(field != value for value in values),

    ItemComparisonOperators.In:
        lambda field, values: or_(field.in_(values)),

    ItemComparisonOperators.Not_in:
        lambda field, values: or_(field.notin_(values)),

    MatchingPatternInStringBase.case_insensitive:
        lambda field, values: or_(field.ilike(value) for value in values),

    MatchingPatternInStringBase.case_sensitive:
        lambda field, values: or_(field.like(value) for value in values),

    MatchingPatternInStringBase.not_case_insensitive:
        lambda field, values: or_(field.not_ilike(value) for value in values),

    MatchingPatternInStringBase.not_case_sensitive:
        lambda field, values: or_(field.not_like(value) for value in values),

    MatchingPatternInStringBase.contains:
        lambda field, values: or_(field.contains(value) for value in values),

    PGSQLMatchingPatternInString.similar_to:
        lambda field, values: or_(field.op("SIMILAR TO")(value) for value in values),

    PGSQLMatchingPatternInString.not_similar_to:
        lambda field, values: or_(field.op("NOT SIMILAR TO")(value) for value in values),

    PGSQLMatchingPatternInString.match_regex_with_case_sensitive:
        lambda field, values: or_(field.op("~")(value) for value in values),

    PGSQLMatchingPatternInString.match_regex_with_case_insensitive:
        lambda field, values: or_(field.op("~*")(value) for value in values),

    PGSQLMatchingPatternInString.does_not_match_regex_with_case_sensitive:
        lambda field, values: or_(field.op("!~")(value) for value in values),

    PGSQLMatchingPatternInString.does_not_match_regex_with_case_insensitive:
        lambda field, values: or_(field.op("!~*")(value) for value in values)
}


class Ordering(StrEnum):
    DESC = auto()
    ASC = auto()
