"""POSEPATH-01: category/output requirements select one provider at config time."""
from dataclasses import asdict, dataclass
import math

POSE_MODEL = 'RFDETRKeypointPreview'
PERSON_MODELS = frozenset(('RFDETRMedium', POSE_MODEL))
JOINT_NAMES = ('nose', 'left_eye', 'right_eye', 'left_ear', 'right_ear',
               'left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow',
               'left_wrist', 'right_wrist', 'left_hip', 'right_hip',
               'left_knee', 'right_knee', 'left_ankle', 'right_ankle')


@dataclass(frozen=True)
class PoseConfig:
    enabled: bool = False
    show_bbox: bool = True
    show_joints: bool = False
    show_edges: bool = False
    person_score: float = .25
    joint_score: float = .15
    min_joints: int = 10
    nms_enabled: bool = True
    nms_iou_threshold: float = .85
    detail_track_id: int | None = None
    detail_object_ref: str | None = None
    detail_frame_seq: int | None = None
    show_joint_names: bool = False
    show_joint_scores: bool = False
    capture_interval_s: float = 3.

    @classmethod
    def parse(cls, value):
        if not isinstance(value, dict) or set(value)-set(cls.__dataclass_fields__):
            raise ValueError('Invalid Pose settings.')
        return cls(**value)

    def __post_init__(self):
        for name in ('enabled','show_bbox','show_joints','show_edges','show_joint_names','show_joint_scores','nms_enabled'):
            if type(getattr(self,name)) is not bool:
                raise ValueError('Invalid Pose toggle.')
        for value in (self.person_score,self.joint_score,self.nms_iou_threshold):
            if type(value) not in (int,float) or not math.isfinite(value) or not 0 <= value <= 1:
                raise ValueError('Invalid Pose score.')
        if type(self.min_joints) is not int or not 1 <= self.min_joints <= 17:
            raise ValueError('Invalid minimum joint count.')
        if (type(self.capture_interval_s) not in (int,float) or not math.isfinite(self.capture_interval_s)
                or not .5 <= self.capture_interval_s <= 30):
            raise ValueError('Invalid capture interval.')
        if self.detail_track_id is not None and (type(self.detail_track_id) is not int or self.detail_track_id < 1):
            raise ValueError('Invalid detail track.')
        if self.detail_object_ref is not None:
            if (type(self.detail_object_ref) is not str or not self.detail_object_ref.isascii()
                    or not self.detail_object_ref.isdigit() or len(self.detail_object_ref)>3
                    or not 0 <= int(self.detail_object_ref)<300
                    or type(self.detail_frame_seq) is not int or self.detail_frame_seq<0
                    or self.detail_track_id is not None):
                raise ValueError('Object detail requires exact Frame reference.')
        elif self.detail_frame_seq is not None:
            raise ValueError('Frame detail requires object reference.')

    @property
    def required(self):
        return self.enabled or self.show_joints or self.show_edges

    def payload(self):
        return asdict(self)


def execution_plan(spec):
    """Compatibility summary of the PR13 provider plan.

    Single-provider callers retain category/model/role/outputs keys. Multi-provider
    callers also receive a providers list; actual Runtime composition uses
    app.provider_plan directly.
    """
    from app.provider_plan import plan as provider_plan
    planned = provider_plan(spec)
    owner = next((p for p in planned.providers if p.person_owner), None)
    primary = owner or planned.providers[0]
    category = ('person' if primary.key in ('medium', 'pose')
                else 'fire_smoke' if primary.key == 'fire' else 'crowd')
    return dict(category=category, model=primary.model, role=primary.role,
                outputs=list(primary.outputs),
                providers=[p.payload() for p in planned.providers],
                person_owner=planned.person_owner)


def planned_profile(spec):
    """Legacy single-provider profile projection used only by rollback path."""
    plan = execution_plan(spec)
    providers = plan.get('providers') or []
    if len(providers) > 1:
        raise ValueError('Multi-provider configuration requires single-decode Runtime.')
    raw = dict(spec.get('inference_profile') or {})
    raw['roles'] = [plan['role']]
    raw[plan['role']+'_model'] = plan['model']
    return raw
