import ast
import hashlib
import json
import os
import time
import urllib.request
from importlib.metadata import distribution, version
from pathlib import Path

import psycopg2
import redis
from psycopg2.extras import RealDictCursor

sid = 'camera-1fdaa47a21ac5365934a42c53f6d327f'


def get(path):
    with urllib.request.urlopen('http://127.0.0.1:5000' + path, timeout=5) as response:
        return json.load(response)


def snapshot():
    state = get('/api/v1/streams/' + sid)
    return {k:state[k] for k in ('stream_id','state','desired_state','observed_state','run_id',
            'run_generation','last_error','cleanup_pending','restart_pending','started_at','uptime_s',
            'metadata_path','scenarios') if k in state}


first = snapshot()
settings = get('/api/v1/streams/' + sid + '/settings')
global_status = get('/api/v1/metadata/status')
client = redis.from_url(os.environ['CONTEXTRA_METADATA_REDIS_URL'], decode_responses=True,
                       socket_connect_timeout=1, socket_timeout=1, max_connections=1)
prefix = os.environ.get('CONTEXTRA_METADATA_NAMESPACE', 'contextra:metadata:v1') + ':camera:' + hashlib.sha256(b'24').hexdigest()
tail = client.xrevrange(prefix + ':frames', count=1)
cursor = tail[0][0] if tail else '0-0'
started_ms = time.time_ns() // 1000000
start = time.monotonic()
frames = []
while time.monotonic() - start < 75:
    for _, entries in client.xread({prefix + ':frames':cursor}, count=50, block=250):
        for entry_id, fields in entries:
            cursor = entry_id
            frames.append(dict(redis_id=entry_id, observed_at_ms=time.time_ns()//1000000,
                               snapshot=json.loads(fields['json'])))
            assert len(frames) <= 1000
ended_ms = time.time_ns() // 1000000
client.close()
last = snapshot()
global_after = get('/api/v1/metadata/status')
with psycopg2.connect(os.environ['DATABASE_URL'], connect_timeout=3,
        options='-c statement_timeout=2500 -c default_transaction_read_only=on') as conn:
    with conn.cursor(cursor_factory=RealDictCursor) as cursor:
        cursor.execute("""SELECT event_id,payload->>'runId' AS run_id,
            payload->>'frameSeq' AS frame_seq,artifact_state,delivery_state,http_status,
            error,delivery_error,created_at,updated_at,media
            FROM contextra_event_artifacts WHERE created_at >= %s
            AND payload->>'cameraId'='24' ORDER BY created_at DESC LIMIT 80""", (time.time()-3600,))
        events = [dict(r) for r in cursor.fetchall()]
    conn.rollback()
path = Path(distribution('supervision').locate_file('supervision/tracker/byte_tracker/core.py'))
text = path.read_text()
tree = ast.parse(text)
klass = next(n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == 'ByteTrack')
methods = {n.name:ast.get_source_segment(text,n) for n in klass.body
           if isinstance(n, ast.FunctionDef) and n.name in ('__init__','update_with_tensors','update_with_detections')}
names = ('app/fight_r5/episode.py','app/fight_r5/runtime.py','app/fight_r5/core.py',
         'app/fight_r5/decision.py','app/event_cooldown.py','app/byte_tracking.py')
print(json.dumps(dict(started_ms=started_ms, ended_ms=ended_ms, before=first, after=last,
    settings={k:{n:v[n] for n in ('fight','tracking','analysisFpsTarget') if n in v}
              if isinstance(v,dict) else v for k,v in settings.items()
              if k in ('configured','effective','restart_pending')},
    artifact_service_before=global_status.get('artifacts'), artifact_service_after=global_after.get('artifacts'),
    frames=frames, recent_events=events, supervision=dict(version=version('supervision'),
       source_sha256=hashlib.sha256(path.read_bytes()).hexdigest(), methods=methods),
    source_sha256={n:hashlib.sha256((Path('/opt/contextra')/n).read_bytes()).hexdigest() for n in names})))
