"""
Silver – {{ title }}
{{ '=' * (title | length + 10) }}
{{ description }}

Bronze input: {{ bronze_source_name }}
Output: Typed, cleansed Silver table with quality rules applied.

Called by lakelogic via external_logic in {{ contract_filename }}.
Generated by Zeus AI from template: silver_cleansed_processor.
"""

from __future__ import annotations

from datetime import datetime, timezone

import polars as pl


def run(
    good_df,
    *,
    contract=None,
    engine: str = "polars",
    **_kwargs,
):
    """
    LakeLogic external_logic entrypoint — Silver Cleansed.

    Transforms raw Bronze data into a typed, cleansed Silver table.
    Applies type casting, deduplication, null handling, and
    quality rule enforcement.

    Parameters
    ----------
    good_df : DataFrame
        Bronze records that passed initial quality rules.
    """
    # ── Coerce to Polars ──────────────────────────────────────────────────
    df = _to_polars(good_df)

{%- if type_casts %}

    # ── Type casting ──────────────────────────────────────────────────────
    df = df.cast({
{%- for cast in type_casts %}
        "{{ cast.column }}": pl.{{ cast.polars_type }},
{%- endfor %}
    })
{%- endif %}

{%- if dedup_key %}

    # ── Deduplicate ───────────────────────────────────────────────────────
    df = df.unique(subset={{ dedup_key | tojson }}, keep="last")
{%- endif %}

{%- if null_defaults %}

    # ── Null handling ─────────────────────────────────────────────────────
    df = df.with_columns(
{%- for nd in null_defaults %}
        pl.col("{{ nd.column }}").fill_null({{ nd.default_value }}).alias("{{ nd.column }}"),
{%- endfor %}
    )
{%- endif %}

{%- if derived_columns %}

    # ── Derived columns ───────────────────────────────────────────────────
    df = df.with_columns(
{%- for derived in derived_columns %}
        {{ derived.expression }}.alias("{{ derived.name }}"),
{%- endfor %}
    )
{%- endif %}

    # ── Add audit/metadata columns ────────────────────────────────────────
    df = df.with_columns(
        pl.lit(datetime.now(timezone.utc).replace(microsecond=0)).alias("_silver_processed_at"),
    )

    return _maybe_to_spark(df, engine)


# ── Helpers ───────────────────────────────────────────────────────────────────


def _to_polars(df) -> pl.DataFrame:
    """Convert any DataFrame to Polars."""
    if isinstance(df, pl.DataFrame):
        return df
    if hasattr(df, "toPandas"):
        return pl.from_pandas(df.toPandas())
    return pl.from_pandas(df)


def _maybe_to_spark(df: pl.DataFrame, engine: str):
    """Convert back to Spark if needed."""
    if engine == "spark":
        try:
            from pyspark.sql import SparkSession
            spark = SparkSession.builder.getOrCreate()
            return spark.createDataFrame(df.to_pandas())
        except Exception:
            pass
    return df
