"""Render template resolution service.

Used from Celery tasks (sync context) to find the best matching .blend template
for a given category + output type combination.

Cascade priority (first active match wins):
1. Exact:         category_key + output_type linked via M2M
2. Category only: category_key + no output_types linked
3. OT only:       category_key IS NULL + output_type linked via M2M
4. Global:        category_key IS NULL + no output_types linked
5. No template  → caller falls back to factory-settings behavior
"""
import logging

from sqlalchemy import create_engine, select, and_, exists
from sqlalchemy.orm import Session

from app.models.render_template import RenderTemplate
from app.models.system_setting import SystemSetting
from app.domains.rendering.models import render_template_output_types

logger = logging.getLogger(__name__)

_engine = None


def _get_engine():
    global _engine
    if _engine is None:
        from app.config import settings as app_settings
        _engine = create_engine(app_settings.database_url_sync)
    return _engine


def resolve_template(
    category_key: str | None = None,
    output_type_id: str | None = None,
) -> RenderTemplate | None:
    """Find the best matching active render template.

    Uses the M2M render_template_output_types table for output type matching.
    Uses sync SQLAlchemy — safe for Celery tasks.
    """
    engine = _get_engine()
    with Session(engine) as session:
        active = RenderTemplate.is_active == True  # noqa: E712

        # Helper: subquery checking if a template is linked to a specific OT
        def _has_ot(ot_id):
            return exists(
                select(render_template_output_types.c.template_id).where(and_(
                    render_template_output_types.c.template_id == RenderTemplate.id,
                    render_template_output_types.c.output_type_id == ot_id,
                ))
            )

        # Helper: subquery checking if a template has NO linked OTs
        _no_ots = ~exists(
            select(render_template_output_types.c.template_id).where(
                render_template_output_types.c.template_id == RenderTemplate.id,
            )
        )

        # 1. Exact match: category_key + output_type in M2M
        if category_key and output_type_id:
            row = session.execute(
                select(RenderTemplate).where(and_(
                    active,
                    RenderTemplate.category_key == category_key,
                    _has_ot(output_type_id),
                ))
            ).unique().scalar_one_or_none()
            if row:
                return row

        # 2. Category only: category_key + no OTs linked
        if category_key:
            row = session.execute(
                select(RenderTemplate).where(and_(
                    active,
                    RenderTemplate.category_key == category_key,
                    _no_ots,
                ))
            ).unique().scalar_one_or_none()
            if row:
                return row

        # 3. OT only: no category_key + output_type in M2M
        if output_type_id:
            row = session.execute(
                select(RenderTemplate).where(and_(
                    active,
                    RenderTemplate.category_key.is_(None),
                    _has_ot(output_type_id),
                ))
            ).unique().scalar_one_or_none()
            if row:
                return row

        # 4. Global fallback: no category_key + no OTs linked
        row = session.execute(
            select(RenderTemplate).where(and_(
                active,
                RenderTemplate.category_key.is_(None),
                _no_ots,
            ))
        ).scalar_one_or_none()
        return row


def get_material_library_path() -> str | None:
    """Return the blend_file_path of the first active AssetLibrary.

    Falls back to the legacy material_library_path system setting.
    """
    engine = _get_engine()
    with Session(engine) as session:
        # Prefer active AssetLibrary
        from app.domains.materials.models import AssetLibrary
        row = session.execute(
            select(AssetLibrary).where(AssetLibrary.is_active == True).limit(1)  # noqa: E712
        ).scalar_one_or_none()
        if row and row.blend_file_path:
            return row.blend_file_path

        # Fallback to legacy system setting
        row = session.execute(
            select(SystemSetting).where(SystemSetting.key == "material_library_path")
        ).scalar_one_or_none()
        if row and row.value and row.value.strip():
            return row.value.strip()
        return None
