"""MROI-01: immutable internal snapshots, independent from external ROI IDs."""
from dataclasses import dataclass
import hashlib
import json

from app.intrusion import inside

FULL_FRAME = ((0., 0.), (0., 1.), (1., 1.), (1., 0.))
CONTRACT = 'multi_roi.internal.v1'


def canonical(value):
    return json.dumps(value, sort_keys=True, separators=(',', ':'), allow_nan=False)


@dataclass(frozen=True)
class Area:
    roi_id: int
    points: tuple
    outside: bool = False
    label: str = 'ROI'
    kind: str = 'polygon'
    reserved: bool = False

    def contains(self, point):
        if not (0 <= point[0] <= 1 and 0 <= point[1] <= 1):
            return False
        hit = inside(point, self.points)
        return not hit if self.outside else hit

    def payload(self):
        return dict(roiId=self.roi_id, points=[list(p) for p in self.points],
                    outside=self.outside, label=self.label, kind=self.kind, reserved=self.reserved)


@dataclass(frozen=True)
class Rule:
    rule_id: int
    roi_id: int
    scenario: str
    enabled: bool
    settings_json: str
    cooldown: float

    @property
    def settings(self):
        return json.loads(self.settings_json)

    def payload(self):
        return dict(ruleId=self.rule_id, roiId=self.roi_id, scenarioCode=self.scenario,
                    enabled=self.enabled, settings=self.settings, cooldownSeconds=self.cooldown)


@dataclass(frozen=True)
class Snapshot:
    camera_id: int
    source_json: str
    areas: tuple[Area, ...]
    rules: tuple[Rule, ...]

    def payload(self):
        return dict(contract=CONTRACT, cameraId=self.camera_id, source=json.loads(self.source_json),
                    rois=[r.payload() for r in self.areas], rules=[r.payload() for r in self.rules])

    @property
    def fingerprint(self):
        return hashlib.sha256(canonical(self.payload()).encode()).hexdigest()

    def summary(self):
        return dict(cameraId=self.camera_id, roiCount=len(self.areas), ruleCount=len(self.rules),
                    roiIds=[a.roi_id for a in self.areas], ruleIds=[r.rule_id for r in self.rules],
                    fingerprint=self.fingerprint)

    @classmethod
    def restore(cls, value):
        # Protected spec only. External requests always pass adapter.normalize first.
        if value.get('contract') != CONTRACT:
            raise ValueError('Unknown internal snapshot.')
        return cls(value['cameraId'], canonical(value['source']),
                   tuple(Area(a['roiId'], tuple(tuple(p) for p in a['points']),
                              a['outside'], a['label'], a['kind'], a.get('reserved', False)) for a in value['rois']),
                   tuple(Rule(r['ruleId'], r['roiId'], r['scenarioCode'], r['enabled'],
                              canonical(r['settings']), r['cooldownSeconds']) for r in value['rules']))
