"""
Functions module for Mock Spark.

This module provides comprehensive mock implementations of PySpark functions
that behave identically to the real PySpark functions for testing and development.
Includes column functions, aggregate functions, window functions, and utility functions.

Key Features:
    - Complete PySpark function API compatibility
    - Column operations (select, filter, transform)
    - String functions (upper, lower, length, trim, regexp_replace, split)
    - Math functions (abs, round, ceil, floor, sqrt, exp, log, pow, sin, cos, tan)
    - Aggregate functions (count, sum, avg, max, min, stddev, variance)
    - DateTime functions (current_timestamp, current_date, to_date, to_timestamp)
    - Window functions (row_number, rank, dense_rank, lag, lead)
    - Conditional functions (when, coalesce, isnull, isnotnull, isnan, nvl, nvl2)
    - Type-safe operations with proper return types

Example:
    >>> from mock_spark.sql import SparkSession, functions as F
    >>> spark = SparkSession("test")
    >>> data = [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 30}]
    >>> df = spark.createDataFrame(data)
    >>> df.select(
    ...     F.upper(F.col("name")).alias("upper_name"),
    ...     F.col("age") * 2,
    ...     F.when(F.col("age") > 25, "senior").otherwise("junior")
    ... ).show()
    DataFrame[2 rows, 3 columns]

    upper_name (age * 2) CASE WHEN ((age > 25)) THEN senior ELSE junior END
    ALICE        50          junior
    BOB          60          senior
"""

from .core.column import Column, ColumnOperation
from .core.literals import Literal
from .core.expressions import ExpressionFunctions
from .base import AggregateFunction
from .conditional import CaseWhen
from .window_execution import WindowFunction
from .functions import Functions, F
from .string import StringFunctions
from .math import MathFunctions
from .aggregate import AggregateFunctions
from .datetime import DateTimeFunctions
from .array import ArrayFunctions
from .map import MapFunctions
from .crypto import CryptoFunctions  # noqa: F401
from .udf import UserDefinedFunction, UserDefinedTableFunction
from .pandas_types import PandasUDFType

# Note: Module-level function aliases are NOT defined here.
# All function access is handled by __getattr__ at the end of this file.
# This allows dynamic function access - all functions are available.
#
# To use functions, import from this module:
#   from mock_spark.functions import col, lit, when
# or use the F namespace:
#   from mock_spark import F
#   F.col("name")

__all__ = [
    "Column",
    "ColumnOperation",
    "Literal",
    "ExpressionFunctions",
    "AggregateFunction",
    "CaseWhen",
    "WindowFunction",
    "Functions",
    "F",
    "StringFunctions",
    "MathFunctions",
    "AggregateFunctions",
    "DateTimeFunctions",
    "ArrayFunctions",
    "MapFunctions",
    # Module-level function aliases
    "col",
    "lit",
    "when",
    "case_when",
    "coalesce",
    "isnull",
    "isnotnull",
    "isnan",
    "nvl",
    "nvl2",
    "upper",
    "lower",
    "length",
    "trim",
    "ltrim",
    "rtrim",
    "regexp_replace",
    "split",
    "substring",
    "concat",
    "expr",
    "format_string",
    "translate",
    "ascii",
    "base64",
    "unbase64",
    "md5",
    "sha1",
    "sha2",
    "sha",
    "mask",
    "json_array_length",
    "json_object_keys",
    "xpath_number",
    "user",
    "crc32",
    "aes_encrypt",
    "aes_decrypt",
    "try_aes_decrypt",
    "regexp_extract_all",
    "array_join",
    "repeat",
    "initcap",
    "soundex",
    "concat_ws",
    "regexp_extract",
    "substring_index",
    "format_number",
    "instr",
    "locate",
    "lpad",
    "rpad",
    "levenshtein",
    "abs",
    "round",
    "ceil",
    "floor",
    "sqrt",
    "exp",
    "log",
    "log10",
    "log2",
    "log1p",
    "expm1",
    "pow",
    "sin",
    "cos",
    "tan",
    "acos",
    "asin",
    "atan",
    "atan2",
    "cosh",
    "sinh",
    "tanh",
    "degrees",
    "radians",
    "cbrt",
    "factorial",
    "rand",
    "randn",
    "rint",
    "bround",
    "sign",
    "width_bucket",
    "greatest",
    "least",
    "count",
    "countDistinct",
    "sum",
    "avg",
    "max",
    "min",
    "first",
    "last",
    "collect_list",
    "collect_set",
    "stddev",
    "variance",
    "skewness",
    "kurtosis",
    "percentile_approx",
    "corr",
    "covar_samp",
    "mean",
    "approx_count_distinct",
    "stddev_pop",
    "stddev_samp",
    "var_pop",
    "var_samp",
    "covar_pop",
    "current_timestamp",
    "current_date",
    "to_date",
    "to_timestamp",
    "hour",
    "day",
    "dayofmonth",
    "month",
    "year",
    "dayofweek",
    "dayofyear",
    "weekofyear",
    "quarter",
    "minute",
    "second",
    "timestamp_seconds",
    "raise_error",
    "add_months",
    "months_between",
    "date_add",
    "date_sub",
    "date_format",
    "from_unixtime",
    "timestampadd",
    "timestampdiff",
    "date_trunc",
    "datediff",
    "date_diff",
    "date_from_unix_date",
    "to_timestamp_ltz",
    "to_timestamp_ntz",
    "unix_timestamp",
    "last_day",
    "next_day",
    "trunc",
    "row_number",
    "rank",
    "dense_rank",
    "lag",
    "lead",
    "first_value",
    "last_value",
    "nth_value",
    "ntile",
    "cume_dist",
    "percent_rank",
    "desc",
    "array",
    "array_repeat",
    "sort_array",
    "array_distinct",
    "array_intersect",
    "array_union",
    "array_except",
    "array_position",
    "array_remove",
    "transform",
    "filter",
    "exists",
    "forall",
    "aggregate",
    "zip_with",
    "array_compact",
    "slice",
    "element_at",
    "array_append",
    "array_prepend",
    "array_insert",
    "array_size",
    "array_sort",
    "arrays_overlap",
    "array_contains",
    "array_max",
    "array_min",
    "explode",
    "size",
    "flatten",
    "reverse",
    "map_keys",
    "map_values",
    "map_entries",
    "map_concat",
    "map_from_arrays",
    "create_map",
    "map_contains_key",
    "map_from_entries",
    "transform_keys",
    "transform_values",
    "map_filter",
    "map_zip_with",
    "struct",
    "named_struct",
    "bit_count",
    "bit_get",
    "bitmap_bit_position",
    "bitmap_bucket_number",
    "bitmap_construct_agg",
    "bitmap_count",
    "bitmap_or_agg",
    "getbit",
    "bitwise_not",
    "convert_timezone",
    "current_timezone",
    "from_utc_timestamp",
    "to_utc_timestamp",
    "parse_url",
    "url_encode",
    "url_decode",
    "date_part",
    "dayname",
    "assert_true",
    "from_xml",
    "to_xml",
    "schema_of_xml",
    "xpath",
    "xpath_boolean",
    "xpath_double",
    "xpath_float",
    "xpath_int",
    "xpath_long",
    "xpath_short",
    "xpath_string",
    # v2.7.0 functions
    "acosh",
    "asinh",
    "atanh",
    "overlay",
    "make_date",
    "bool_and",
    "bool_or",
    "every",
    "some",
    "max_by",
    "min_by",
    "count_if",
    "any_value",
    "version",
    # Phase 2: Advanced Features (PySpark 3.0)
    "mean",
    "approx_count_distinct",
    "count_distinct",
    "stddev_pop",
    "stddev_samp",
    "var_pop",
    "var_samp",
    "covar_pop",
    "explode_outer",
    "posexplode",
    "posexplode_outer",
    "arrays_zip",
    "sequence",
    "shuffle",
    "from_json",
    "to_json",
    "get_json_object",
    "json_tuple",
    "schema_of_json",
    "from_csv",
    "to_csv",
    "schema_of_csv",
    # Phase 3: Specialized Functions (PySpark 3.0)
    "hypot",
    "nanvl",
    "signum",
    "bin",
    "hex",
    "unhex",
    "hash",
    "xxhash64",
    "encode",
    "decode",
    "conv",
    "asc",
    "asc_nulls_first",
    "asc_nulls_last",
    "desc_nulls_first",
    "desc_nulls_last",
    "input_file_name",
    "monotonically_increasing_id",
    "spark_partition_id",
    "broadcast",
    "column",
    "grouping",
    "grouping_id",
    # New functions from implementation plan
    "char_length",
    "character_length",
    "btrim",
    "contains",
    "left",
    "right",
    "bit_length",
    "weekday",
    "extract",
    "median",
    "mode",
    "percentile",
    "approx_percentile",
    "ifnull",
    "nullif",
    "try_add",
    "try_subtract",
    "try_multiply",
    "try_divide",
    "try_sum",
    "try_avg",
    "try_element_at",
    "try_to_binary",
    "try_to_number",
    "try_to_timestamp",
    "array_agg",
    "cardinality",
    "cot",
    "csc",
    "sec",
    "e",
    "pi",
    "ln",
    "ceiling",
    "bit_and",
    "bit_or",
    "bit_xor",
    # Top 10 new features
    "udf",
    "window",
    "approxCountDistinct",
    "sumDistinct",
    "bitwiseNOT",
    "toDegrees",
    "toRadians",
    # Phase 1: High-priority missing features
    "pandas_udf",
    "UserDefinedFunction",
    "UserDefinedTableFunction",
    # Final core features for 100% PySpark 3.0-3.5 coverage
    "PandasUDFType",
    "to_str",
    # PySpark 3.4+ features
    "window_time",
    # Next 25 missing features
    "startswith",
    "endswith",
    "like",
    "rlike",
    "replace",
    "substr",
    "split_part",
    "position",
    "octet_length",
    "char",
    "ucase",
    "lcase",
    "elt",
    "power",
    "std",
    "product",
    "positive",
    "negative",
    "sum_distinct",
    "now",
    "curdate",
    "days",
    "hours",
    "months",
    "equal_null",
    # Next 50 missing features
    "ilike",
    "find_in_set",
    "regexp_count",
    "regexp_like",
    "regexp_substr",
    "regexp_instr",
    "regexp",
    "sentences",
    "printf",
    "to_char",
    "to_varchar",
    "pmod",
    "negate",
    "shiftleft",
    "shiftright",
    "shiftrightunsigned",
    "shiftLeft",
    "shiftRight",
    "shiftRightUnsigned",
    "years",
    "localtimestamp",
    "dateadd",
    "datepart",
    "make_timestamp",
    "make_timestamp_ltz",
    "make_timestamp_ntz",
    "make_interval",
    "make_dt_interval",
    "make_ym_interval",
    "to_number",
    "to_binary",
    "to_unix_timestamp",
    "unix_date",
    "unix_seconds",
    "unix_millis",
    "unix_micros",
    "timestamp_seconds",
    "timestamp_millis",
    "timestamp_micros",
    "regr_avgx",
    "regr_avgy",
    "regr_count",
    "regr_intercept",
    "regr_r2",
    "regr_slope",
    "regr_sxx",
    "regr_sxy",
    "regr_syy",
    "cast",
    "stack",
    "str_to_map",
    "typeof",
    "get",
    "inline",
    "inline_outer",
    # Next 50 missing features (crypto, string, math, datetime, try functions)
    "aes_encrypt",
    "aes_decrypt",
    "try_aes_decrypt",
    "sha",
    "mask",
    "json_array_length",
    "json_object_keys",
    "xpath_number",
    "user",
    "getbit",
    "width_bucket",
    "approx_percentile",
    "date_from_unix_date",
    "to_timestamp_ltz",
    "to_timestamp_ntz",
    "try_add",
    "try_subtract",
    "try_multiply",
    "try_divide",
    "try_sum",
    "try_avg",
    "try_element_at",
    "try_to_binary",
    "try_to_number",
    "try_to_timestamp",
]

from typing import Any  # noqa: E402


def __getattr__(name: str) -> Any:
    """
    Custom attribute access for function imports.

    This function is called when accessing any attribute that isn't already defined
    in the module. It enables `from mock_spark.functions import function_name` syntax.

    Args:
        name: Name of the function being accessed

    Returns:
        The requested function/attribute

    Raises:
        AttributeError: If function not found
    """
    # Check if this is a known function
    if name in __all__:
        # Try to return it from F
        try:
            return getattr(F, name)
        except AttributeError:
            # Function in __all__ but not in F - might be a class or other export
            pass

    # Not found
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


# Populate module namespace with ALL functions from F
# This enables `from mock_spark.functions import function_name` syntax
_CLASS_EXPORTS = {
    "Column",
    "ColumnOperation",
    "Literal",
    "ExpressionFunctions",
    "AggregateFunction",
    "CaseWhen",
    "WindowFunction",
    "Functions",
    "F",
    "StringFunctions",
    "MathFunctions",
    "AggregateFunctions",
    "DateTimeFunctions",
    "ArrayFunctions",
    "MapFunctions",
    "BitwiseFunctions",
    "XMLFunctions",
    "CryptoFunctions",
    "UserDefinedFunction",
    "UserDefinedTableFunction",
    "PandasUDFType",
}

# Add ALL functions to module namespace unconditionally
# (Version checking via environment variable or F.function_name access)
for _name in __all__:
    if _name not in _CLASS_EXPORTS and hasattr(F, _name):
        globals()[_name] = getattr(F, _name)
