from sqlalchemy import CheckConstraint

__all__ = [
    "ArrayElementsPositive",
    "ArrayNotEmpty",
    "IsPgTimeZoneName",
    "JsonbArray",
    "JsonbObject",
    "LessOrEqual",
    "LessOrEqualThanValue",
    "LessThan",
    "NotNegative",
    "OneOf",
    "Positive",
    "StringNotEmpty",
]


class NotNegative(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"{field} >= 0"
        name = f"{field}_not_negative"
        super().__init__(sqltext, name, *args, **kwargs)


class Positive(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"{field} > 0.0"
        name = f"{field}_positive"
        super().__init__(sqltext, name, *args, **kwargs)


class LessThan(CheckConstraint):
    def __init__(self, lhs, rhs, *args, **kwargs):
        sqltext = f"{lhs} < {rhs}"
        name = f"{lhs}_lt_{rhs}"
        super().__init__(sqltext, name, *args, **kwargs)


class LessOrEqualThanValue(CheckConstraint):
    def __init__(self, field, max_value, *args, **kwargs):
        sqltext = f"{field} <= {max_value}"
        max_value_str = str(max_value).replace(".", "_")
        name = f"{field}_le_{max_value_str}"
        super().__init__(sqltext, name, *args, **kwargs)


class LessOrEqual(CheckConstraint):
    def __init__(self, lhs, rhs, *args, **kwargs):
        sqltext = f"{lhs} <= {rhs}"
        name = f"{lhs}_le_{rhs}"
        super().__init__(sqltext, name, *args, **kwargs)


class JsonbObject(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"jsonb_typeof({field}) = 'object'"
        name = f"{field}_jsonb_object"
        super().__init__(sqltext, name, *args, **kwargs)


class JsonbArray(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"jsonb_typeof({field}) = 'array'"
        name = f"{field}_jsonb_array"
        super().__init__(sqltext, name, *args, **kwargs)


class ArrayNotEmpty(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"cardinality({field}) > 0"
        name = f"{field}_not_empty_array"
        super().__init__(sqltext, name, *args, **kwargs)


class ArrayElementsPositive(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"0 < all({field})"
        name = f"{field}_array_elements_positive"
        super().__init__(sqltext, name, *args, **kwargs)


class StringNotEmpty(CheckConstraint):
    def __init__(self, field, *args, **kwargs):
        sqltext = f"length({field}) > 0"
        name = f"{field}_not_empty_string"
        super().__init__(sqltext, name, *args, **kwargs)


class OneOf(CheckConstraint):
    def __init__(self, *fields, **kwargs):
        conditions = []
        for field in fields:
            conditions.append(f"({field} is not null)::integer")
        sqltext = " + ".join(conditions) + " = 1"
        name = f"one_of_{'_'.join(fields)}"
        super().__init__(sqltext, name, **kwargs)


class OptionalOneOf(CheckConstraint):
    def __init__(self, *fields, **kwargs):
        conditions = []
        for field in fields:
            conditions.append(f"({field} is not null)::integer")
        sqltext = " + ".join(conditions) + " < 2"
        name = f"optional_one_of_{'_'.join(fields)}"
        super().__init__(sqltext, name, **kwargs)


class IsPgTimeZoneName(CheckConstraint):
    def __init__(self, field, **kwargs):
        sqltext = f"now() AT TIME ZONE {field} IS NOT NULL"
        name = f"{field}_is_pg_timezone"
        super().__init__(sqltext, name, **kwargs)
