from __future__ import annotations

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

import dill as pickle
import pytest
from inference.models.qwen3vl.qwen3vl_inference_models import \
    InferenceModelsQwen3VLAdapter
from inference_models import PreProcessingOverrides


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

def _make_adapter_without_init():
    """
    Create an InferenceModelsQwen3VLAdapter instance without calling __init__.
    This avoids invoking AutoModel.from_pretrained and other heavy I/O operations.
    """
    return object.__new__(InferenceModelsQwen3VLAdapter)

def test_map_inference_kwargs_all_true_sets_overrides_and_preserves_other_keys():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    adapter = _make_adapter_without_init()
    original_kwargs = {'disable_preproc_contrast': True, 'disable_preproc_grayscale': True, 'disable_preproc_static_crop': True, 'unrelated_key': 'keep_me'}
    _call__bound__arguments = inspect.signature(adapter.map_inference_kwargs).bind(original_kwargs)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(adapter.map_inference_kwargs, 'tests.inference.unit_tests.test_map_inference_kwargs__unit_test_0', None, 'test_map_inference_kwargs_all_true_sets_overrides_and_preserves_other_keys', 'map_inference_kwargs', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    returned = codeflash_output
    preproc = returned['pre_processing_overrides']

def test_map_inference_kwargs_defaults_when_flags_missing():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    adapter = _make_adapter_without_init()
    kwargs = {}
    _call__bound__arguments = inspect.signature(adapter.map_inference_kwargs).bind(kwargs)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(adapter.map_inference_kwargs, 'tests.inference.unit_tests.test_map_inference_kwargs__unit_test_0', None, 'test_map_inference_kwargs_defaults_when_flags_missing', 'map_inference_kwargs', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    out = codeflash_output
    preproc = out['pre_processing_overrides']

def test_map_inference_kwargs_overwrites_existing_pre_processing_overrides_value():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    adapter = _make_adapter_without_init()
    existing_value = {'foo': 'bar'}
    kwargs = {'pre_processing_overrides': existing_value, 'disable_preproc_contrast': False, 'disable_preproc_grayscale': False, 'disable_preproc_static_crop': False}
    _call__bound__arguments = inspect.signature(adapter.map_inference_kwargs).bind(kwargs)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(adapter.map_inference_kwargs, 'tests.inference.unit_tests.test_map_inference_kwargs__unit_test_0', None, 'test_map_inference_kwargs_overwrites_existing_pre_processing_overrides_value', 'map_inference_kwargs', '3', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    out = codeflash_output

def test_map_inference_kwargs_with_numeric_and_none_values():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    adapter = _make_adapter_without_init()
    kwargs = {'disable_preproc_contrast': 1, 'disable_preproc_grayscale': 0, 'disable_preproc_static_crop': None}
    _call__bound__arguments = inspect.signature(adapter.map_inference_kwargs).bind(kwargs)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(adapter.map_inference_kwargs, 'tests.inference.unit_tests.test_map_inference_kwargs__unit_test_0', None, 'test_map_inference_kwargs_with_numeric_and_none_values', 'map_inference_kwargs', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    out = codeflash_output
    preproc = out['pre_processing_overrides']

def test_map_inference_kwargs_preserves_other_complex_values():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    adapter = _make_adapter_without_init()
    large_list = [i for i in range(10)]
    kwargs = {'disable_preproc_contrast': False, 'some_list': large_list}
    _call__bound__arguments = inspect.signature(adapter.map_inference_kwargs).bind(kwargs)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(adapter.map_inference_kwargs, 'tests.inference.unit_tests.test_map_inference_kwargs__unit_test_0', None, 'test_map_inference_kwargs_preserves_other_complex_values', 'map_inference_kwargs', '3', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_output = codeflash_return_value
    out = codeflash_output

def test_map_inference_kwargs_repeated_invocations_do_not_leak_state():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    adapter = _make_adapter_without_init()
    iterations = 1000
    for i in range(iterations):
        kwargs = {'disable_preproc_contrast': i % 3 == 0, 'disable_preproc_grayscale': i % 3 == 1}
        _call__bound__arguments = inspect.signature(adapter.map_inference_kwargs).bind(kwargs)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(adapter.map_inference_kwargs, 'tests.inference.unit_tests.test_map_inference_kwargs__unit_test_0', None, 'test_map_inference_kwargs_repeated_invocations_do_not_leak_state', 'map_inference_kwargs', '2_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
        codeflash_output = codeflash_return_value
        out = codeflash_output
        preproc = out['pre_processing_overrides']