"""CAD processing tasks for the products domain."""
from __future__ import annotations
import asyncio
import logging

from celery import shared_task

logger = logging.getLogger(__name__)


@shared_task(name="products.extract_mesh_attributes", queue="step_processing", bind=True)
def extract_mesh_attributes(self, cad_file_id: str) -> dict:
    """Extract mesh topology attributes from a STEP file using pythonOCC.

    Stores result in cad_files.mesh_attributes JSONB.
    Returns attribute dict.
    """
    from app.database import AsyncSessionLocal

    async def _run() -> dict:
        from sqlalchemy import select, update as sql_update
        from app.domains.products.models import CadFile

        async with AsyncSessionLocal() as db:
            result = await db.execute(select(CadFile).where(CadFile.id == cad_file_id))
            cad_file = result.scalar_one_or_none()
            if not cad_file:
                logger.warning("CadFile %s not found", cad_file_id)
                return {}

            try:
                attrs = _extract_from_step(cad_file.stored_path)
            except Exception as exc:
                logger.error("Mesh extraction failed for %s: %s", cad_file_id, exc)
                attrs = {"error": str(exc)}

            await db.execute(
                sql_update(CadFile)
                .where(CadFile.id == cad_file_id)
                .values(mesh_attributes=attrs)
            )
            await db.commit()
            return attrs

    return asyncio.get_event_loop().run_until_complete(_run())


def _extract_from_step(step_path: str, threshold_deg: float = 30.0) -> dict:
    """Use pythonOCC to analyse STEP topology.

    Returns face_groups, counts, and meta-information.
    Gracefully returns {} when OCC is not installed.
    """
    try:
        from OCC.Core.STEPControl import STEPControl_Reader
        from OCC.Core.TopAbs import TopAbs_EDGE, TopAbs_FACE
        from OCC.Core.TopExp import TopExp_Explorer
        from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
        from OCC.Core.GeomAbs import (
            GeomAbs_Plane, GeomAbs_Cylinder, GeomAbs_Torus,
            GeomAbs_Cone, GeomAbs_Sphere,
        )
    except ImportError:
        logger.warning("pythonOCC not available — skipping mesh extraction")
        return {}

    reader = STEPControl_Reader()
    if reader.ReadFile(step_path) != 1:
        raise ValueError(f"STEP read failed: {step_path}")
    reader.TransferRoots()
    shape = reader.OneShape()

    face_groups: dict[str, int] = {
        "planar": 0, "cylindrical": 0, "toroidal": 0,
        "conical": 0, "spherical": 0, "other": 0,
    }
    type_map = {
        GeomAbs_Plane: "planar",
        GeomAbs_Cylinder: "cylindrical",
        GeomAbs_Torus: "toroidal",
        GeomAbs_Cone: "conical",
        GeomAbs_Sphere: "spherical",
    }

    face_explorer = TopExp_Explorer(shape, TopAbs_FACE)
    face_count = 0
    while face_explorer.More():
        adaptor = BRepAdaptor_Surface(face_explorer.Current())
        key = type_map.get(adaptor.GetType(), "other")
        face_groups[key] += 1
        face_count += 1
        face_explorer.Next()

    edge_explorer = TopExp_Explorer(shape, TopAbs_EDGE)
    edge_count = 0
    while edge_explorer.More():
        edge_count += 1
        edge_explorer.Next()

    curved = face_groups["cylindrical"] + face_groups["toroidal"] + face_groups["spherical"]

    return {
        "face_count": face_count,
        "edge_count": edge_count,
        "face_groups": face_groups,
        "curved_ratio": round(curved / max(face_count, 1), 3),
        "sharp_angle_threshold_deg": threshold_deg,
        "extraction_version": "1.0",
    }
