from __future__ import annotations

import datetime
import gc
import os
import time

import pytest
from inference.core.workflows.core_steps.analytics.detection_event_log.v1 import \
    DetectionEventLogBlockV1
from inference.core.workflows.execution_engine.entities.base import \
    VideoMetadata


def codeflash_wrap(codeflash_wrapped, codeflash_test_module_name, codeflash_test_class_name, codeflash_test_name, codeflash_function_name, codeflash_line_id, codeflash_loop_index, *args, **kwargs):
    test_id = f'{codeflash_test_module_name}:{codeflash_test_class_name}:{codeflash_test_name}:{codeflash_line_id}:{codeflash_loop_index}'
    if not hasattr(codeflash_wrap, 'index'):
        codeflash_wrap.index = {}
    if test_id in codeflash_wrap.index:
        codeflash_wrap.index[test_id] += 1
    else:
        codeflash_wrap.index[test_id] = 0
    codeflash_test_index = codeflash_wrap.index[test_id]
    invocation_id = f'{codeflash_line_id}_{codeflash_test_index}'
    test_stdout_tag = f"{codeflash_test_module_name}:{(codeflash_test_class_name + '.' if codeflash_test_class_name else '')}{codeflash_test_name}:{codeflash_function_name}:{codeflash_loop_index}:{invocation_id}"
    print(f'!$######{test_stdout_tag}######$!')
    exception = None
    gc.disable()
    try:
        counter = time.perf_counter_ns()
        return_value = codeflash_wrapped(*args, **kwargs)
        codeflash_duration = time.perf_counter_ns() - counter
    except Exception as e:
        codeflash_duration = time.perf_counter_ns() - counter
        exception = e
    gc.enable()
    print(f'!######{test_stdout_tag}:{codeflash_duration}######!')
    if exception:
        raise exception
    return return_value

def test_frame_timestamp_initializes_and_returns_zero():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    ts = datetime.datetime(2020, 1, 1, 0, 0, 0)
    metadata = VideoMetadata(frame_timestamp=ts, fps=30.0)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_frame_timestamp_initializes_and_returns_zero', '_get_relative_time', '3', codeflash_loop_index, video_id='video_1', current_frame=1, metadata=metadata, fallback_fps=25.0)
    codeflash_output = codeflash_return_value
    rel = codeflash_output
    stored = block._first_frame_timestamps.get('video_1')

def test_frame_timestamp_returns_correct_delta_on_later_frames():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    base_ts = datetime.datetime(2020, 1, 1, 0, 0, 0)
    metadata_init = VideoMetadata(frame_timestamp=base_ts, fps=30.0)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_frame_timestamp_returns_correct_delta_on_later_frames', '_get_relative_time', '3', codeflash_loop_index, 'v2', 1, metadata_init, fallback_fps=10.0)
    later_ts = base_ts + datetime.timedelta(seconds=2, microseconds=500000)
    metadata_later = VideoMetadata(frame_timestamp=later_ts, fps=30.0)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_frame_timestamp_returns_correct_delta_on_later_frames', '_get_relative_time', '6', codeflash_loop_index, 'v2', 10, metadata_later, fallback_fps=10.0)
    codeflash_output = codeflash_return_value
    rel = codeflash_output

def test_frame_timestamp_can_produce_negative_relative_time_if_earlier():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    init_ts = datetime.datetime(2020, 1, 1, 0, 0, 10)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_frame_timestamp_can_produce_negative_relative_time_if_earlier', '_get_relative_time', '2', codeflash_loop_index, 'v3', 1, VideoMetadata(frame_timestamp=init_ts, fps=30.0), fallback_fps=10.0)
    earlier_ts = datetime.datetime(2020, 1, 1, 0, 0, 5)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_frame_timestamp_can_produce_negative_relative_time_if_earlier', '_get_relative_time', '4', codeflash_loop_index, 'v3', 2, VideoMetadata(frame_timestamp=earlier_ts, fps=30.0), fallback_fps=10.0)
    codeflash_output = codeflash_return_value
    rel = codeflash_output

def test_fallback_uses_metadata_fps_when_present_and_nonzero():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    metadata = VideoMetadata(frame_timestamp=None, fps=10.0)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_fallback_uses_metadata_fps_when_present_and_nonzero', '_get_relative_time', '2', codeflash_loop_index, 'v_fps', 6, metadata, fallback_fps=30.0)
    codeflash_output = codeflash_return_value
    rel = codeflash_output

def test_fallback_uses_fallback_fps_when_metadata_fps_is_none_or_zero():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    metadata_none = VideoMetadata(frame_timestamp=None, fps=None)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_fallback_uses_fallback_fps_when_metadata_fps_is_none_or_zero', '_get_relative_time', '2', codeflash_loop_index, 'v_fb1', 11, metadata_none, fallback_fps=20.0)
    codeflash_output = codeflash_return_value
    rel_none = codeflash_output
    metadata_zero = VideoMetadata(frame_timestamp=None, fps=0)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_fallback_uses_fallback_fps_when_metadata_fps_is_none_or_zero', '_get_relative_time', '5', codeflash_loop_index, 'v_fb2', 21, metadata_zero, fallback_fps=10.0)
    codeflash_output = codeflash_return_value
    rel_zero = codeflash_output

def test_negative_and_zero_current_frame_behavior():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    metadata = VideoMetadata(frame_timestamp=None, fps=10.0)

def test_negative_metadata_fps_is_respected_and_can_yield_negative_time():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    metadata_neg = VideoMetadata(frame_timestamp=None, fps=-5.0)
    codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_negative_metadata_fps_is_respected_and_can_yield_negative_time', '_get_relative_time', '2', codeflash_loop_index, 'v_negfps', 3, metadata_neg, fallback_fps=30.0)
    codeflash_output = codeflash_return_value
    rel = codeflash_output

def test_large_scale_many_videos_and_persistence_of_first_timestamp():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    block = DetectionEventLogBlockV1()
    base = datetime.datetime(2021, 1, 1, 0, 0, 0)
    N = 1000
    for i in range(N):
        vid = f'bulk_vid_{i}'
        ts = base + datetime.timedelta(seconds=i)
        metadata = VideoMetadata(frame_timestamp=ts, fps=25.0)
        codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_large_scale_many_videos_and_persistence_of_first_timestamp', '_get_relative_time', '3_3', codeflash_loop_index, vid, 1, metadata, fallback_fps=30.0)
        codeflash_output = codeflash_return_value
        rel = codeflash_output
    target_vid = 'bulk_vid_500'
    first_stored = block._first_frame_timestamps[target_vid]
    last_rel = -1.0
    for extra in range(1, 1001):
        new_ts = base + datetime.timedelta(seconds=500 + extra)
        metadata_later = VideoMetadata(frame_timestamp=new_ts, fps=25.0)
        codeflash_return_value = codeflash_wrap(block._get_relative_time, 'tests.inference.unit_tests.test__get_relative_time__unit_test_0', None, 'test_large_scale_many_videos_and_persistence_of_first_timestamp', '_get_relative_time', '7_2', codeflash_loop_index, target_vid, 1 + extra, metadata_later, fallback_fps=30.0)
        codeflash_output = codeflash_return_value
        rel = codeflash_output
        last_rel = rel