# generated by datamodel-codegen:
#   filename:  VizQLDataServiceOpenAPISchema.json
#   timestamp: 2025-08-20T20:30:50+00:00

from __future__ import annotations

from datetime import date
from enum import Enum
from typing import Annotated, Any, Dict, List, Optional, Union

from .tableau_model import TableauModel
from pydantic import AwareDatetime, Field as PydanticField, RootModel


class TableauError(TableauModel):
    errorCode: Optional[str] = None
    message: Optional[str] = None
    datetime: Optional[AwareDatetime] = None
    debug: Optional[Dict[str, Any]] = None
    tab_error_code: Annotated[Optional[str], PydanticField(alias='tab-error-code')] = None


class DataType(Enum):
    INTEGER = 'INTEGER'
    REAL = 'REAL'
    STRING = 'STRING'
    DATETIME = 'DATETIME'
    BOOLEAN = 'BOOLEAN'
    DATE = 'DATE'
    SPATIAL = 'SPATIAL'
    UNKNOWN = 'UNKNOWN'


class Connection(TableauModel):
    connectionLuid: Optional[str] = None
    connectionUsername: str
    connectionPassword: str


class Datasource(TableauModel):
    datasourceLuid: Annotated[
        str, PydanticField(description='The LUID of the data source to be queried.')
    ]
    connections: Optional[List[Connection]] = None


class FilterType(Enum):
    QUANTITATIVE_DATE = 'QUANTITATIVE_DATE'
    QUANTITATIVE_NUMERICAL = 'QUANTITATIVE_NUMERICAL'
    SET = 'SET'
    MATCH = 'MATCH'
    DATE = 'DATE'
    TOP = 'TOP'


class DimensionFilterField(TableauModel):
    fieldCaption: Annotated[
        str, PydanticField(description='The caption of the field to filter on.')
    ]


class CalculatedFilterField(TableauModel):
    calculation: Annotated[
        str, PydanticField(description='A Tableau calculation that will be used to filter on.')
    ]


class Function(Enum):
    SUM = 'SUM'
    AVG = 'AVG'
    MEDIAN = 'MEDIAN'
    COUNT = 'COUNT'
    COUNTD = 'COUNTD'
    MIN = 'MIN'
    MAX = 'MAX'
    STDEV = 'STDEV'
    VAR = 'VAR'
    COLLECT = 'COLLECT'
    YEAR = 'YEAR'
    QUARTER = 'QUARTER'
    MONTH = 'MONTH'
    WEEK = 'WEEK'
    DAY = 'DAY'
    TRUNC_YEAR = 'TRUNC_YEAR'
    TRUNC_QUARTER = 'TRUNC_QUARTER'
    TRUNC_MONTH = 'TRUNC_MONTH'
    TRUNC_WEEK = 'TRUNC_WEEK'
    TRUNC_DAY = 'TRUNC_DAY'
    AGG = 'AGG'
    NONE = 'NONE'
    UNSPECIFIED = 'UNSPECIFIED'


class QuantitativeFilterType(Enum):
    RANGE = 'RANGE'
    MIN = 'MIN'
    MAX = 'MAX'
    ONLY_NULL = 'ONLY_NULL'
    ONLY_NON_NULL = 'ONLY_NON_NULL'


class QueryOutput(TableauModel):
    data: Optional[List] = None


class ReturnFormat(Enum):
    OBJECTS = 'OBJECTS'
    ARRAYS = 'ARRAYS'


class SortDirection(Enum):
    ASC = 'ASC'
    DESC = 'DESC'


class PeriodType(Enum):
    MINUTES = 'MINUTES'
    HOURS = 'HOURS'
    DAYS = 'DAYS'
    WEEKS = 'WEEKS'
    MONTHS = 'MONTHS'
    QUARTERS = 'QUARTERS'
    YEARS = 'YEARS'


class DateRangeType(Enum):
    CURRENT = 'CURRENT'
    LAST = 'LAST'
    LASTN = 'LASTN'
    NEXT = 'NEXT'
    NEXTN = 'NEXTN'
    TODATE = 'TODATE'


class Direction(Enum):
    TOP = 'TOP'
    BOTTOM = 'BOTTOM'


class FieldBase(TableauModel):
    fieldCaption: Annotated[
        str,
        PydanticField(
            description='Either the name of a specific Field in the data source, or, in the case of a calculation, a user-supplied name for the calculation.'
        ),
    ]
    fieldAlias: Annotated[
        Optional[str],
        PydanticField(
            description='An alternative name to give the field. Will only be used in object format output.'
        ),
    ] = None
    maxDecimalPlaces: Annotated[
        Optional[int],
        PydanticField(
            description='The maximum number of decimal places. Any trailing 0s will be dropped. The maxDecimalPlaces value must be greater or equal to 0.'
        ),
    ] = None
    sortDirection: Optional[SortDirection] = None
    sortPriority: Annotated[
        Optional[int],
        PydanticField(
            description='To enable sorting on a specific Field, provide a sortPriority for that field, and that field will be sorted. The sortPriority provides a ranking of how to sort fields when multiple fields are being sorted. The highest priority (lowest number) field is sorted first. If only one field is being sorted, then any value may be used for sortPriority. SortPriority should be an integer greater than 0.'
        ),
    ] = None


class DimensionField(FieldBase):
    fieldCaption: Optional[Any] = None
    fieldAlias: Optional[Any] = None
    maxDecimalPlaces: Optional[Any] = None
    sortDirection: Optional[Any] = None
    sortPriority: Optional[Any] = None


class MeasureField(FieldBase):
    function: Function
    fieldCaption: Optional[Any] = None
    fieldAlias: Optional[Any] = None
    maxDecimalPlaces: Optional[Any] = None
    sortDirection: Optional[Any] = None
    sortPriority: Optional[Any] = None


class CalculatedField(FieldBase):
    calculation: Annotated[
        str,
        PydanticField(
            description='A Tableau calculation that will be returned as a Field in the query.'
        ),
    ]
    fieldCaption: Optional[Any] = None
    fieldAlias: Optional[Any] = None
    maxDecimalPlaces: Optional[Any] = None
    sortDirection: Optional[Any] = None
    sortPriority: Optional[Any] = None


class FieldMetadata(TableauModel):
    fieldName: Optional[str] = None
    fieldCaption: Optional[str] = None
    dataType: Optional[DataType] = None
    defaultAggregation: Optional[Function] = None
    logicalTableId: Optional[str] = None


class MeasureFilterField(TableauModel):
    fieldCaption: Annotated[
        str, PydanticField(description='The caption of the field to filter on.')
    ]
    function: Function


class MetadataOutput(TableauModel):
    data: Optional[List[FieldMetadata]] = None


class QueryOptions(TableauModel):
    returnFormat: Optional[ReturnFormat] = None
    debug: Optional[bool] = False


class ReadMetadataRequest(TableauModel):
    datasource: Datasource
    options: Optional[QueryOptions] = None


class Field(RootModel[Union[DimensionField, MeasureField, CalculatedField]]):
    root: Union[DimensionField, MeasureField, CalculatedField]


class FilterField(
    RootModel[Union[DimensionFilterField, MeasureFilterField, CalculatedFilterField]]
):
    root: Union[DimensionFilterField, MeasureFilterField, CalculatedFilterField]


class QueryDatasourceOptions(QueryOptions):
    disaggregate: Optional[bool] = False


class Filter(TableauModel):
    field: FilterField
    filterType: FilterType
    context: Annotated[
        Optional[bool],
        PydanticField(
            description="Make the given filter a context filter, meaning that it's an independent filter. Any other filters that you set are defined as dependent filters because they process only the data that passes through the context filter."
        ),
    ] = False


class MatchFilter(Filter):
    contains: Annotated[
        Optional[str], PydanticField(description='Matches when a field contains this value.')
    ] = None
    startsWith: Annotated[
        Optional[str], PydanticField(description='Matches when a field starts with this value.')
    ] = None
    endsWith: Annotated[
        Optional[str], PydanticField(description='Matches when a field ends with this value.')
    ] = None
    exclude: Annotated[
        Optional[bool],
        PydanticField(description='When true, the inverse of the matching logic will be used.'),
    ] = False


class QuantitativeFilterBase(Filter):
    quantitativeFilterType: QuantitativeFilterType
    includeNulls: Annotated[
        Optional[bool],
        PydanticField(
            description='Should nulls be returned or not. Only applies to RANGE, MIN, and MAX filters. If not provided, the default is to not include null values.'
        ),
    ] = None


class QuantitativeNumericalFilter(QuantitativeFilterBase):
    min: Annotated[
        Optional[float],
        PydanticField(
            description='A numerical value, either integer or floating point, indicating the minimum value to filter upon. Required if using quantitativeFilterType RANGE or if using quantitativeFilterType MIN.'
        ),
    ] = None
    max: Annotated[
        Optional[float],
        PydanticField(
            description='A numerical value, either integer or floating point, indicating the maximum value to filter upon. Required if using quantitativeFilterType RANGE or if using quantitativeFilterType MIN.'
        ),
    ] = None


class QuantitativeDateFilter(QuantitativeFilterBase):
    minDate: Annotated[
        Optional[date],
        PydanticField(
            description='An RFC 3339 date indicating the earliest date to filter upon. Required if using quantitativeFilterType RANGE or if using quantitativeFilterType MIN.'
        ),
    ] = None
    maxDate: Annotated[
        Optional[date],
        PydanticField(
            description='An RFC 3339 date indicating the latest date to filter on. Required if using quantitativeFilterType RANGE or if using quantitativeFilterType MIN.'
        ),
    ] = None


class Query(TableauModel):
    fields: Annotated[
        List[Field], PydanticField(description='An array of fields that define the query.')
    ]
    filters: Annotated[
        Optional[List[TabFilter]],
        PydanticField(description='An optional array of filters to apply to the query.'),
    ] = None


class QueryRequest(TableauModel):
    datasource: Datasource
    query: Query
    options: Optional[QueryDatasourceOptions] = None


class SetFilter(Filter):
    values: Annotated[List[Any], PydanticField(description='An array of values to filter on.')]
    exclude: Optional[bool] = False


class RelativeDateFilter(Filter):
    periodType: Annotated[
        PeriodType, PydanticField(description='The units of time in the relative date range.')
    ]
    dateRangeType: Annotated[
        DateRangeType, PydanticField(description='The direction in the relative date range.')
    ]
    rangeN: Annotated[
        Optional[int],
        PydanticField(
            description='When dateRangeType is LASTN or NEXTN, this is the N value (how many years, months, etc.).'
        ),
    ] = None
    anchorDate: Annotated[
        Optional[date],
        PydanticField(
            description="If a value for this field isn't provided, the value defaults to today."
        ),
    ] = None
    includeNulls: Annotated[
        Optional[bool],
        PydanticField(
            description="Should nulls be returned or not. If a value isn't provided, the default is to not include null values."
        ),
    ] = None


class TopNFilter(Filter):
    direction: Annotated[
        Optional[Direction],
        PydanticField(description='Top (ascending) or Bottom (descending) N.'),
    ] = 'TOP'
    howMany: Annotated[
        int,
        PydanticField(
            description='The number of values from the top or the bottom of the given fieldToMeasure.'
        ),
    ]
    fieldToMeasure: FilterField

class TabFilter(RootModel[Union[
    MatchFilter, QuantitativeNumericalFilter, QuantitativeDateFilter, SetFilter, RelativeDateFilter, TopNFilter]]):
    root: Union[
        MatchFilter, QuantitativeNumericalFilter, QuantitativeDateFilter, SetFilter, RelativeDateFilter, TopNFilter]
