from __future__ import annotations

import gc
import os
import time
from datetime import datetime, timedelta
from pathlib import Path

import pytest
from inference.core.managers.inference_models_cache_watchdog import (
    BYTES_IN_MB, LOCK_POSTFIX, list_files)


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_nonexistent_path_returns_empty(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    not_exists = tmp_path / 'does_not_exist_dir'
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_nonexistent_path_returns_empty', 'list_files', '1', codeflash_loop_index, str(not_exists))
    codeflash_output = codeflash_return_value
    result = codeflash_output

def test_symlink_path_returns_empty(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    target = tmp_path / 'real_file.txt'
    target.write_text('hello')
    symlink = tmp_path / 'link_to_real'
    try:
        os.symlink(str(target), str(symlink))
    except (OSError, NotImplementedError) as e:
        pytest.skip(f'Symlink not supported on this platform: {e}')
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_symlink_path_returns_empty', 'list_files', '4', codeflash_loop_index, str(symlink))
    codeflash_output = codeflash_return_value
    result = codeflash_output

def test_file_path_returns_single_fileinfo(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    f = tmp_path / 'single.bin'
    data = b'0123456789' * 10
    f.write_bytes(data)
    now = time.time()
    os.utime(str(f), (now, now))
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_file_path_returns_single_fileinfo', 'list_files', '5', codeflash_loop_index, str(f))
    codeflash_output = codeflash_return_value
    result = codeflash_output
    fi = result[0]
    expected_mb = len(data) / BYTES_IN_MB

def test_file_with_lock_postfix_is_ignored(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    lock_name = 'resource' + LOCK_POSTFIX
    f = tmp_path / lock_name
    f.write_text('locked')
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_file_with_lock_postfix_is_ignored', 'list_files', '3', codeflash_loop_index, str(f))
    codeflash_output = codeflash_return_value
    result = codeflash_output
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_file_with_lock_postfix_is_ignored', 'list_files', '5', codeflash_loop_index, str(tmp_path))
    codeflash_output = codeflash_return_value
    result_dir = codeflash_output
    returned_paths = [getattr(fi, 'path') for fi in result_dir]

def test_directory_walk_filters_lock_and_symlink_and_handles_nested(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    dir_a = tmp_path / 'a'
    dir_b = dir_a / 'b'
    dir_b.mkdir(parents=True)
    file1 = dir_a / 'file1.txt'
    file1.write_text('one')
    file2 = dir_b / 'file2.log'
    file2.write_text('two')
    file_lock = dir_b / ('ignored' + LOCK_POSTFIX)
    file_lock.write_text('should be ignored')
    real = dir_a / 'real.txt'
    real.write_text('real content')
    symlink = dir_b / 'to_real.symlink'
    try:
        os.symlink(str(real), str(symlink))
    except (OSError, NotImplementedError):
        symlink = None
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_directory_walk_filters_lock_and_symlink_and_handles_nested', 'list_files', '13', codeflash_loop_index, str(dir_a))
    codeflash_output = codeflash_return_value
    results = codeflash_output
    returned = {os.path.abspath(getattr(fi, 'path')) for fi in results}
    expected = {os.path.abspath(str(file1)), os.path.abspath(str(file2)), os.path.abspath(str(real))}
    for p in returned:
        pass

def test_special_character_and_unicode_filenames(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    names = ['space file.txt', 'weird_#$_file.bin', 'unicode_файл.txt']
    created = []
    for nm in names:
        p = tmp_path / nm
        p.write_text('x')
        created.append(os.path.abspath(str(p)))
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_special_character_and_unicode_filenames', 'list_files', '3', codeflash_loop_index, str(tmp_path))
    codeflash_output = codeflash_return_value
    results = codeflash_output
    returned_paths = {getattr(fi, 'path') for fi in results}
    for p in created:
        pass

def test_large_scale_many_files(tmp_path):
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    n = 1000
    expected_paths = []
    for i in range(n):
        p = tmp_path / f'file_{i}.dat'
        p.write_bytes(b'0123456789')
        expected_paths.append(os.path.abspath(str(p)))
    for i in range(5):
        lp = tmp_path / f'ignored_{i}{LOCK_POSTFIX}'
        lp.write_text('ignore')
    codeflash_return_value = codeflash_wrap(list_files, 'tests.inference.unit_tests.test_list_files__unit_test_0', None, 'test_large_scale_many_files', 'list_files', '4', codeflash_loop_index, str(tmp_path))
    codeflash_output = codeflash_return_value
    results = codeflash_output
    returned_paths = {getattr(fi, 'path') for fi in results}
    sample = results[0]
    for fi in results[:5]:
        pass