"""Temporary FIGHT adapter: shared Pose/Tracking, independent pair Events and ROI."""
from dataclasses import dataclass, fields, replace
import time
import uuid

from app.fight_config import FightConfig
from app.fight_scenario import FightEngine
from app.intrusion import IntrusionConfig, IntrusionEvent, inside


@dataclass(frozen=True)
class FightSettings:
    policy: FightConfig
    geometry: IntrusionConfig
    scenario_name = 'FIGHT'

    @classmethod
    def parse(cls, value):
        names = {f.name for f in fields(FightConfig)}
        roi_names = {'roi', 'roi_id', 'roi_name', 'debug_roi'}
        if type(value) is not dict or set(value)-names-roi_names:
            raise ValueError('Invalid FIGHT settings.')
        policy = FightConfig.parse({k:v for k,v in value.items() if k in names})
        roi=value.get('roi')
        geometry = IntrusionConfig(enabled=policy.enabled,confidence=policy.person_score,
            roi=((0.,0.),(1.,0.),(1.,1.),(0.,1.)) if roi is None or roi == [] else roi,
            roi_id=value.get('roi_id','primary'),roi_name=value.get('roi_name','Fight ROI'),
            debug_roi=value.get('debug_roi',True))
        return cls(policy,geometry)

    @property
    def enabled(self): return self.policy.enabled
    @property
    def confidence(self): return self.policy.person_score
    @property
    def roi(self): return self.geometry.roi
    @property
    def roi_name(self): return self.geometry.roi_name
    @property
    def debug_roi(self): return self.geometry.debug_roi
    def payload(self):
        return {k:v for k,v in self.geometry.payload().items()
                if k in ('enabled','confidence','roi','roi_id','roi_name','debug_roi','point_x','point_y')}
    def settings_payload(self):
        return dict(self.policy.payload(),**{k:v for k,v in self.geometry.payload().items()
                    if k in ('roi','roi_id','roi_name','debug_roi')})


@dataclass(frozen=True)
class FightDisplay:
    point: tuple
    inside: bool
    state: str
    confidence: float
    event_id: str | None = None
    def payload(self):
        return dict(scenario='FIGHT',point=list(self.point),inside=self.inside,state=self.state,
                    confidence=self.confidence,eventId=self.event_id)


class FightEvent(IntrusionEvent):
    def payload(self): return dict(super().payload(),scenario='FIGHT')


class FightScenario:
    def __init__(self,config,event_id_factory=lambda:str(uuid.uuid4()),wall_ns=time.time_ns):
        self.config=config;self.engine=FightEngine(config.policy)
        self._events={};self.event_id_factory=event_id_factory;self.wall_ns=wall_ns

    def evaluate(self,frame,poses,ids,*,gap_before=False):
        points=tuple(((p.bbox_xyxy[0]+p.bbox_xyxy[2])/2,p.bbox_xyxy[3]) for p in poses)
        selected=[i for i,p in enumerate(points) if inside(p,self.config.roi)]
        result=self.engine.evaluate(frame,tuple(poses[i] for i in selected),tuple(ids[i] for i in selected),gap_before=gap_before)
        rows=[(FightDisplay(p,i in selected,'MONITORING' if i in selected else 'OUTSIDE',self.config.confidence),) for i,p in enumerate(points)]
        if not result.accepted:return tuple(rows),()
        active={p.track_ids for p in result.pairs if p.alert}
        # Pair state remains engine-owned; keep bounded UUIDs through brief missing/ROI gaps.
        self._events={k:v for k,v in self._events.items() if k in self.engine.latched_pairs()}
        events=[]
        for event in result.event_candidates:
            i=selected[int(event.object_refs[0])]
            eid=self.event_id_factory();self._events[event.track_ids]=eid
            events.append(FightEvent(eid,frame,event.track_ids[0],self.config.geometry.roi_id,str(i),
                poses[i].score,poses[i].bbox_xyxy,poses[i].model_key,self.wall_ns()//1000000))
        for pair in active:
            for i in selected:
                if ids[i] in pair:
                    if rows[i][0].state == 'MONITORING':
                        rows[i] = ()
                    rows[i]+= (FightDisplay(points[i],True,'FIGHT',self.config.confidence,self._events.get(pair)),)
        return tuple(rows),tuple(events)

    def clear(self): self.engine.clear();self._events.clear()


def project_fight(spec,scenarios):
    req=next(s for s in scenarios if s['scenarioCode']=='FIGHT')
    if not req['enabled'] and 'fight' not in spec:return
    roi=req['rois'][0] if req['rois'] else None
    cfg=dict(spec.get('fight') or {},enabled=req['enabled'],person_score=.25 if req['confidence'] is None else req['confidence'],
             roi=roi['points'] if roi else [],roi_name=roi['label'] if roi else 'Fight ROI')
    spec['fight']=FightSettings.parse(cfg).settings_payload()


def validate(spec):
    cfg=FightSettings.parse(spec.get('fight') or {})
    if cfg.enabled and (not spec.get('server_id') or not spec.get('source_id') or (spec.get('tracking') or {}).get('enabled',True) is not True):
        raise ValueError('FIGHT requires canonical identity and shared Tracking.')
    return cfg
