from __future__ import annotations

import gc
import inspect
import os
import time
from pathlib import Path

import dill as pickle
import pytest
from inference.core.workflows.core_steps.loader import load_kinds
from inference.core.workflows.execution_engine.entities.types import (
    BAR_CODE_DETECTION_KIND, BOOLEAN_KIND, BYTES_KIND,
    CLASSIFICATION_PREDICTION_KIND, CONTOURS_KIND, DETECTION_KIND,
    DICTIONARY_KIND, EMBEDDING_KIND, FLOAT_KIND, FLOAT_ZERO_TO_ONE_KIND,
    IMAGE_KEYPOINTS_KIND, IMAGE_KIND, IMAGE_METADATA_KIND, INFERENCE_ID_KIND,
    INSTANCE_SEGMENTATION_PREDICTION_KIND, INTEGER_KIND,
    KEYPOINT_DETECTION_PREDICTION_KIND, LANGUAGE_MODEL_OUTPUT_KIND,
    LIST_OF_VALUES_KIND, NUMPY_ARRAY_KIND, OBJECT_DETECTION_PREDICTION_KIND,
    PARENT_ID_KIND, POINT_KIND, PREDICTION_TYPE_KIND, QR_CODE_DETECTION_KIND,
    RGB_COLOR_KIND, ROBOFLOW_API_KEY_KIND, ROBOFLOW_MANAGED_KEY,
    ROBOFLOW_MODEL_ID_KIND, ROBOFLOW_PROJECT_KIND, SECRET_KIND,
    SEMANTIC_SEGMENTATION_PREDICTION_KIND, SERIALISED_PAYLOADS_KIND,
    STRING_KIND, TIMESTAMP_KIND, TOP_CLASS_KIND, VIDEO_METADATA_KIND,
    WILDCARD_KIND, ZONE_KIND, Kind)


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}######!')
    iteration = os.environ['CODEFLASH_TEST_ITERATION']
    with Path('{codeflash_run_tmp_dir_client_side}', f'test_return_values_{iteration}.bin').open('ab') as f:
        pickled_values = pickle.dumps((args, kwargs, exception)) if exception else pickle.dumps((args, kwargs, return_value))
        _test_name = f"{codeflash_test_module_name}:{(codeflash_test_class_name + '.' if codeflash_test_class_name else '')}{codeflash_test_name}:{codeflash_function_name}:{codeflash_line_id}".encode('ascii')
        f.write(len(_test_name).to_bytes(4, byteorder='big'))
        f.write(_test_name)
        f.write(codeflash_duration.to_bytes(8, byteorder='big'))
        f.write(len(pickled_values).to_bytes(4, byteorder='big'))
        f.write(pickled_values)
        f.write(codeflash_loop_index.to_bytes(8, byteorder='big'))
        f.write(len(invocation_id).to_bytes(4, byteorder='big'))
        f.write(invocation_id.encode('ascii'))
    if exception:
        raise exception
    return return_value
_EXPECTED_KINDS_ORDER = [WILDCARD_KIND, IMAGE_KIND, VIDEO_METADATA_KIND, ROBOFLOW_MODEL_ID_KIND, ROBOFLOW_PROJECT_KIND, ROBOFLOW_API_KEY_KIND, FLOAT_ZERO_TO_ONE_KIND, LIST_OF_VALUES_KIND, SERIALISED_PAYLOADS_KIND, BOOLEAN_KIND, INTEGER_KIND, STRING_KIND, TOP_CLASS_KIND, FLOAT_KIND, DICTIONARY_KIND, DETECTION_KIND, CLASSIFICATION_PREDICTION_KIND, POINT_KIND, ZONE_KIND, OBJECT_DETECTION_PREDICTION_KIND, INSTANCE_SEGMENTATION_PREDICTION_KIND, KEYPOINT_DETECTION_PREDICTION_KIND, SEMANTIC_SEGMENTATION_PREDICTION_KIND, RGB_COLOR_KIND, IMAGE_KEYPOINTS_KIND, CONTOURS_KIND, LANGUAGE_MODEL_OUTPUT_KIND, NUMPY_ARRAY_KIND, QR_CODE_DETECTION_KIND, BAR_CODE_DETECTION_KIND, PREDICTION_TYPE_KIND, ROBOFLOW_MANAGED_KEY, PARENT_ID_KIND, IMAGE_METADATA_KIND, BYTES_KIND, INFERENCE_ID_KIND, SECRET_KIND, EMBEDDING_KIND, TIMESTAMP_KIND]

def test_returns_list_of_kind_instances():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_returns_list_of_kind_instances', 'load_kinds', '0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    kinds = codeflash_output
    for idx, k in enumerate(kinds):
        pass

def test_exact_order_and_identity_matches_expected_constants():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_exact_order_and_identity_matches_expected_constants', 'load_kinds', '0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    kinds = codeflash_output
    for idx, expected in enumerate(_EXPECTED_KINDS_ORDER):
        pass

def test_names_are_non_empty_strings_and_unique():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_names_are_non_empty_strings_and_unique', 'load_kinds', '0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    kinds = codeflash_output
    names = [k.name for k in kinds]
    for idx, n in enumerate(names):
        pass

def test_kinds_are_hashable_and_hashes_distinct():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_kinds_are_hashable_and_hashes_distinct', 'load_kinds', '0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    kinds = codeflash_output
    hashes = []
    for idx, k in enumerate(kinds):
        h = hash(k)
        hashes.append(h)

def test_subsequent_calls_return_new_list_objects_but_same_elements():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_subsequent_calls_return_new_list_objects_but_same_elements', 'load_kinds', '0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    first = codeflash_output
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_subsequent_calls_return_new_list_objects_but_same_elements', 'load_kinds', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    second = codeflash_output
    for i in range(len(first)):
        pass

def test_stability_under_repeated_calls_and_performance():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    iterations = 1000
    expected_len = len(_EXPECTED_KINDS_ORDER)
    for _ in range(iterations):
        _call__bound__arguments = inspect.signature(load_kinds).bind()
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_stability_under_repeated_calls_and_performance', 'load_kinds', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
        codeflash_output = codeflash_return_value
        kinds = codeflash_output

def test_strict_content_equality_to_canonical_list():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    _call__bound__arguments = inspect.signature(load_kinds).bind()
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(load_kinds, 'tests.inference.unit_tests.test_load_kinds__unit_test_0', None, 'test_strict_content_equality_to_canonical_list', 'load_kinds', '0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    got = codeflash_output
    for idx, (g_item, expected_item) in enumerate(zip(got, _EXPECTED_KINDS_ORDER)):
        pass