"""Bounded Crowd-only visualization inside the existing offline renderer."""
from functools import lru_cache
import numpy as np
from PIL import Image, ImageDraw, ImageFont

from app.crowd_config import CROWD_MODEL, FULL_FRAME
from app.intrusion import _inside_edges

COLORS = dict(SAFE='#00CC66', CAUTION='#FFFF00', WARNING='#FF9900', DANGER='#FF3030')


@lru_cache(maxsize=32)
def kernel(radius):
    axis = np.arange(-radius, radius+1, dtype=np.float32)
    return np.exp(-(axis[:, None]**2 + axis[None, :]**2)/(2*max(radius/3, .5)**2))


def draw_crowd(output, snapshot, point, content_width):
    cfg = snapshot.get('scenarioConfig', {}).get('CROWD', {})
    roi = tuple(tuple(p) for p in cfg.get('roi', FULL_FRAME))
    edges = tuple(zip(roi, roi[1:] + roi[:1]))
    polygon = [point(*p) for p in roi]
    regions = (snapshot.get('crowd') or {}).get('regions', [])
    region = regions[0] if regions else None
    width, height = output.size
    if region and region.get('status') in COLORS:
        mw, mh = max(1, min(320, width//4)), max(1, min(180, height//4))
        density = np.zeros((mh, mw), dtype=np.float32)
        for item in snapshot['objects']:
            if (not item['visible'] or item['modelKey'] != CROWD_MODEL or item['classId'] != 1):
                continue
            x1, y1, x2, y2 = item['bbox']
            center = ((x1+x2)/2, (y1+y2)/2)
            if not _inside_edges(center, edges):
                continue
            x, y = point(*center)
            x, y = min(mw-1, round(x*mw/width)), min(mh-1, round(y*mh/height))
            radius = max(12*width/1280, min(40*width/1280, (x2-x1)*content_width*.75))
            r = max(1, round(radius*mw/width))
            stamp = kernel(r)
            left, top, right, bottom = max(0,x-r), max(0,y-r), min(mw,x+r+1), min(mh,y+r+1)
            density[top:bottom, left:right] += stamp[top-y+r:bottom-y+r, left-x+r:right-x+r]
        # Fixed response, not per-frame max normalization: overlap stays meaningful.
        alpha = Image.fromarray(np.rint((1-np.exp(-density))*140).astype(np.uint8))
        alpha = alpha.resize(output.size, Image.Resampling.BILINEAR)
        mask = Image.new('L', output.size)
        ImageDraw.Draw(mask).polygon(polygon, fill=255)
        alpha = Image.fromarray(np.where(np.asarray(mask)>0, np.asarray(alpha), 0).astype(np.uint8))
        output = Image.composite(Image.new('RGB', output.size, COLORS[region['status']]), output, alpha)
    draw = ImageDraw.Draw(output)
    if cfg.get('debug_roi', False):
        draw.line(polygon+[polygon[0]], fill=COLORS['DANGER'], width=max(1, round(width/640)))
    text = (f"Count: {region['count']}\nDensity: {region['densityRatio']*100:.0f}% | {region['status']}"
            if region else 'Count: --\nDensity: -- | NO DATA')
    font_size = max(10, round(20*width/1280))
    while True:
        font = ImageFont.load_default(size=font_size)
        box = draw.multiline_textbbox((0,0), text, font=font, spacing=3)
        tw, th = box[2]-box[0], box[3]-box[1]
        if tw+12 <= width or font_size <= 6:
            break
        font_size -= 1
    x = max(0, min(width-tw-12, max(p[0] for p in polygon)-tw-12))
    y = max(0, min(height-th-12, max(p[1] for p in polygon)-th-12))
    draw.rectangle((x,y,x+tw+12,y+th+12), fill=COLORS['DANGER'])
    draw.multiline_text((x+6-box[0],y+6-box[1]), text, fill='black', font=font, spacing=3)
    return output
