# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
"""
Column model.

This model was introduced in SIP-68 (https://github.com/apache/superset/issues/14909),
and represents a "column" in a table or dataset. In addition to a column, new models for
tables, metrics, and datasets were also introduced.

These models are not fully implemented, and shouldn't be used yet.
"""
import sqlalchemy as sa
from flask_appbuilder import Model

from superset.models.helpers import (
    AuditMixinNullable,
    ExtraJSONMixin,
    ImportExportMixin,
)

UNKOWN_TYPE = "UNKNOWN"


class Column(
    Model,
    AuditMixinNullable,
    ExtraJSONMixin,
    ImportExportMixin,
):
    """
    A "column".

    The definition of column here is overloaded: it can represent a physical column in a
    database relation (table or view); a computed/derived column on a dataset; or an
    aggregation expression representing a metric.
    """

    __tablename__ = "sl_columns"

    id = sa.Column(sa.Integer, primary_key=True)

    # Assuming the column is an aggregation, is it additive? Useful for determining which
    # aggregations can be done on the metric. Eg, ``COUNT(DISTINCT user_id)`` is not
    # additive, so it shouldn't be used in a ``SUM``.
    is_additive = sa.Column(sa.Boolean, default=False)

    # Is this column an aggregation (metric)?
    is_aggregation = sa.Column(sa.Boolean, default=False)

    is_filterable = sa.Column(sa.Boolean, nullable=False, default=True)
    is_dimensional = sa.Column(sa.Boolean, nullable=False, default=False)

    # Is an increase desired? Useful for displaying the results of A/B tests, or setting
    # up alerts. Eg, this is true for "revenue", but false for "latency".
    is_increase_desired = sa.Column(sa.Boolean, default=True)

    # Column is managed externally and should be read-only inside Superset
    is_managed_externally = sa.Column(sa.Boolean, nullable=False, default=False)

    # Is this column a partition? Useful for scheduling queries and previewing the latest
    # data.
    is_partition = sa.Column(sa.Boolean, default=False)

    # Does the expression point directly to a physical column?
    is_physical = sa.Column(sa.Boolean, default=True)

    # Is this a spatial column? This could be leveraged in the future for spatial
    # visualizations.
    is_spatial = sa.Column(sa.Boolean, default=False)

    # Is this a time column? Useful for plotting time series.
    is_temporal = sa.Column(sa.Boolean, default=False)

    # We use ``sa.Text`` for these attributes because (1) in modern databases the
    # performance is the same as ``VARCHAR``[1] and (2) because some table names can be
    # **really** long (eg, Google Sheets URLs).
    #
    # [1] https://www.postgresql.org/docs/9.1/datatype-character.html
    name = sa.Column(sa.Text)
    # Raw type as returned and used by db engine.
    type = sa.Column(sa.Text, default=UNKOWN_TYPE)

    # Assigns column advnaced type to determine custom behavior
    # does nothing unless feature flag ENABLE_ADVANCED_DATA_TYPES in true
    advanced_data_type = sa.Column(sa.Text)

    # Columns are defined by expressions. For tables, these are the actual columns names,
    # and should match the ``name`` attribute. For datasets, these can be any valid SQL
    # expression. If the SQL expression is an aggregation the column is a metric,
    # otherwise it's a computed column.
    expression = sa.Column(sa.Text)
    unit = sa.Column(sa.Text)

    # Additional metadata describing the column.
    description = sa.Column(sa.Text)
    warning_text = sa.Column(sa.Text)
    external_url = sa.Column(sa.Text, nullable=True)

    def __repr__(self) -> str:
        return f"<Column id={self.id}>"
