"""
Gold – {{ title }}
{{ '=' * (title | length + 7) }}
{{ description }}

Silver inputs:
{%- for source in silver_sources %}
  - {{ source.name }} ({{ source.field_count }} fields, PK: {{ source.primary_key | join(', ') }})
{%- endfor %}

Output columns:
{%- for col in output_columns %}
  - {{ col.name }} ({{ col.type }}){% if col.derived %} [derived]{% endif %}
{%- endfor %}

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

from __future__ import annotations

from datetime import datetime, timezone
from typing import Optional

import polars as pl


def run(
    good_df,
    *,
    contract=None,
    engine: str = "polars",
{%- for source in silver_sources[1:] %}
    {{ source.param_name }}: str = "",
{%- endfor %}
    **_kwargs,
):
    """
    LakeLogic external_logic entrypoint.

    Parameters
    ----------
    good_df : DataFrame
        {{ silver_sources[0].name }} records that passed quality rules.
{%- for source in silver_sources[1:] %}
    {{ source.param_name }} : str
        Fully-qualified table name for {{ source.name }}.
{%- endfor %}
    """
    # ── Coerce to Polars ──────────────────────────────────────────────────
    df = _to_polars(good_df)

{%- for source in silver_sources[1:] %}

    # ── Load {{ source.name }} ──────────────────────────────────────────
    {{ source.var_name }} = _try_load_table({{ source.param_name }}, engine)
{%- endfor %}

{% for join in joins %}
    # ── {{ join.description }} ────────────────────────────────────────────
    if {{ join.right_var }} is not None:
{%- if join.aggregate %}
        {{ join.right_var }}_agg = {{ join.right_var }}.group_by("{{ join.on }}").agg(
{%- for agg in join.aggregations %}
            pl.{{ agg.func }}("{{ agg.field }}").alias("{{ agg.alias }}"),
{%- endfor %}
        )
        df = df.join({{ join.right_var }}_agg, on="{{ join.on }}", how="{{ join.how }}")
{%- else %}
{%- if join.select_fields %}
        {{ join.right_var }}_lookup = {{ join.right_var }}.select({{ join.select_fields | tojson }}).unique()
{%- endif %}
        df = df.join({{ join.right_var }}{% if join.select_fields %}_lookup{% endif %}, on="{{ join.on }}", how="{{ join.how }}")
{%- endif %}
    else:
{%- for fallback in join.fallback_columns %}
        df = df.with_columns(pl.lit(None, dtype=pl.{{ fallback.dtype }}).alias("{{ fallback.name }}"))
{%- endfor %}

{% endfor %}
{%- if derived_columns %}
    # ── Derive KPIs ───────────────────────────────────────────────────────
    df = (
        df
{%- for derived in derived_columns %}
        .with_columns(
            {{ derived.expression }}.alias("{{ derived.name }}"),
        )
{%- endfor %}
        # Audit column
        .with_columns(
            pl.lit(datetime.now(timezone.utc).replace(microsecond=0)).alias("gold_processed_at"),
        )
    )
{%- endif %}

    # ── Select final columns ──────────────────────────────────────────────
    output_cols = [
{%- for col in output_columns %}
        "{{ col.name }}",
{%- endfor %}
        "gold_processed_at",
    ]
    available = [c for c in output_cols if c in df.columns]
    result = df.select(available)

    return _maybe_to_spark(result, 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 _try_load_table(table_name: str, engine: str) -> Optional[pl.DataFrame]:
    """Try to load a table. Returns None if unavailable."""
    if not table_name:
        return None
    try:
        if engine == "spark":
            from pyspark.sql import SparkSession
            spark = SparkSession.builder.getOrCreate()
            return pl.from_pandas(spark.table(table_name).toPandas())
        else:
            return pl.read_delta(table_name.replace("table:", ""))
    except Exception:
        return None


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
