# pylint: disable=line-too-long,useless-suppression,too-many-lines
# coding=utf-8
# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# Code generated by Microsoft (R) Python Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------
import pytest
import os
import re
from typing import Tuple, Union, Dict, Any, Optional, List, Set
from devtools_testutils.aio import recorded_by_proxy_async
from testpreparer_async import ContentUnderstandingClientTestBaseAsync, ContentUnderstandingPreparer
from azure.ai.contentunderstanding.models import ContentAnalyzer
from azure.ai.contentunderstanding.aio import ContentUnderstandingClient
from azure.ai.contentunderstanding.models import AnalysisInput
from test_helpers import (
    generate_analyzer_id,
    new_simple_content_analyzer_object,
    new_invoice_analyzer_object,
    new_marketing_video_analyzer_object,
    assert_poller_properties,
    assert_simple_content_analyzer_result,
    assert_invoice_fields,
    assert_document_properties,
    save_analysis_result_to_file,
    save_keyframe_image_to_file,
)
from devtools_testutils import is_live, is_live_and_not_recording


async def create_analyzer_and_assert_async(
    client: ContentUnderstandingClient, analyzer_id: str, resource: Union[ContentAnalyzer, Dict[str, Any]]
) -> Any:
    """Create an analyzer and perform basic assertions (async version).

    Args:
        client: The ContentUnderstandingClient instance
        analyzer_id: The analyzer ID to create
        resource: The analyzer resource (ContentAnalyzer object or dict)

    Returns:
        Any: The poller object

    Raises:
        AssertionError: If the creation fails or assertions fail
    """
    print(f"\nCreating analyzer {analyzer_id}")

    # Start the analyzer creation operation
    poller = await client.begin_create_analyzer(
        analyzer_id=analyzer_id,
        resource=resource,
    )

    # Wait for the operation to complete
    print(f"  Waiting for analyzer {analyzer_id} to be created")
    response = await poller.result()
    assert response is not None
    assert poller.done()
    print(f"  Analyzer {analyzer_id} is created successfully")

    # Additional poller assertions
    assert poller is not None
    assert poller.status() is not None
    assert poller.continuation_token() is not None

    return poller


async def delete_analyzer_and_assert(
    client: ContentUnderstandingClient, analyzer_id: str, created_analyzer: bool
) -> None:
    """Delete an analyzer and assert it was deleted successfully.

    Args:
        client: The ContentUnderstandingClient instance
        analyzer_id: The analyzer ID to delete
        created_analyzer: Whether the analyzer was created (to determine if cleanup is needed)

    Raises:
        AssertionError: If the analyzer still exists after deletion
    """
    if created_analyzer:
        print(f"Cleaning up analyzer {analyzer_id}")
        try:
            await client.delete_analyzer(analyzer_id=analyzer_id)
        except Exception as e:
            # If deletion fails, the test should fail
            raise AssertionError(f"Failed to delete analyzer {analyzer_id}: {e}") from e
    else:
        print(f"Analyzer {analyzer_id} was not created, no cleanup needed")


async def download_keyframes_and_assert_async(
    client: ContentUnderstandingClient,
    analysis_operation_id: str,
    result: Any,
    test_py_file_dir: str,
    identifier: Optional[str] = None,
) -> None:
    """Download keyframes from video analysis result and assert their existence (async version).

    Downloads up to 3 keyframes: first, middle, and last frame to avoid duplicates.

    Args:
        client: The ContentUnderstandingClient instance
        analysis_operation_id: The operation ID from the analysis
        result: The analysis result containing markdown with keyframes
        test_py_file_dir: The directory where pytest files are located
        identifier: Optional unique identifier to avoid conflicts (e.g., analyzer_id)

    Returns:
        None

    Raises:
        AssertionError: If no keyframes are found in the analysis result
    """
    keyframe_ids: Set[str] = set()

    # Iterate over contents to find keyframes from markdown
    for content in result.contents:
        # Extract keyframe IDs from "markdown" if it exists and is a string
        markdown_content = getattr(content, "markdown", "")
        if isinstance(markdown_content, str):
            # Use the same regex pattern as the official sample: (keyFrame\.d+)\.jpg
            keyframe_ids.update(re.findall(r"(keyFrame\.\d+)\.jpg", markdown_content))

    print(f"Found keyframe IDs in markdown: {keyframe_ids}")

    # Assert that keyframe IDs were found in the video analysis
    assert (
        keyframe_ids
    ), "No keyframe IDs were found in the video analysis markdown content. Video analysis should generate keyframes that can be extracted using regex pattern."

    print(f"Successfully extracted {len(keyframe_ids)} keyframe IDs from video analysis")

    # Sort keyframes by frame number to get first, middle, and last
    # Extract numeric part from "keyFrame.22367" format and convert to "keyframes/22367" format
    def extract_frame_number(keyframe_id: str) -> int:
        # Extract number after "keyFrame."
        match = re.search(r"keyFrame\.(\d+)", keyframe_id)
        if match:
            return int(match.group(1))
        return 0

    # Build keyframe paths in the format expected by get_result_file API: "keyframes/{time_ms}"
    keyframe_paths = [f"keyframes/{extract_frame_number(kf)}" for kf in keyframe_ids]

    # Sort by frame number
    sorted_keyframes: List[str] = sorted(keyframe_paths, key=lambda x: int(x.split("/")[-1]))

    # Create a set with first, middle, and last frames (automatically removes duplicates)
    frames_set: Set[str] = {sorted_keyframes[0], sorted_keyframes[-1], sorted_keyframes[len(sorted_keyframes) // 2]}

    # Convert set to list for processing
    frames_to_download: List[str] = list(frames_set)

    print(f"Selected frames to download: {frames_to_download}")

    # Try to retrieve the selected keyframe images using get_result_file API
    files_retrieved: int = 0

    for keyframe_id in frames_to_download:
        print(f"Trying to get result file with path: {keyframe_id}")
        response = await client.get_result_file(
            operation_id=analysis_operation_id,
            path=keyframe_id,  # Use keyframe_id directly as path, no .jpg extension
        )

        # Handle the response - it's an async iterator that needs to be collected
        from collections.abc import AsyncIterator

        assert isinstance(response, AsyncIterator), f"Expected AsyncIterator, got {type(response)}"

        # It's an async iterator, collect all bytes efficiently
        chunks = []
        async for chunk in response:
            chunks.append(chunk)
        result_bytes = b"".join(chunks)

        # Assert that we successfully get a response and it's valid image data
        assert result_bytes is not None, f"Response for path {keyframe_id} should not be None"
        assert isinstance(
            result_bytes, bytes
        ), f"Response for {keyframe_id} should be bytes (image data), got {type(result_bytes)}"
        assert len(result_bytes) > 0, f"Image file content for {keyframe_id} should not be empty"

        # Save the image file using the helper function
        saved_file_path = save_keyframe_image_to_file(
            image_content=result_bytes,
            keyframe_id=keyframe_id,
            test_name="test_content_analyzers_get_result_file",
            test_py_file_dir=test_py_file_dir,
            identifier=identifier,
        )

        # Verify the saved file exists and has content
        assert os.path.exists(saved_file_path), f"Saved image file should exist at {saved_file_path}"
        assert os.path.getsize(saved_file_path) > 0, f"Saved image file should not be empty"

        files_retrieved += 1
        print(f"Successfully downloaded keyframe image: {keyframe_id}")

    # Assert that we successfully downloaded all expected files
    assert files_retrieved == len(
        frames_to_download
    ), f"Expected to download {len(frames_to_download)} files, but only downloaded {files_retrieved}"
    print(f"Successfully completed get_result_file test - downloaded {files_retrieved} keyframe images")


class TestContentUnderstandingContentAnalyzersOperationsAsync(ContentUnderstandingClientTestBaseAsync):

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_update_defaults_async(self, contentunderstanding_endpoint: str, **kwargs) -> Dict[str, str]:
        """
        Tests updating default model deployments for the Content Understanding service.
        Verifies that model deployments (gpt-4.1, gpt-4.1-mini, text-embedding-3-large) can be updated and are correctly persisted.
        """
        # Get variables from test proxy (recorded values in playback, empty dict in recording)
        variables = kwargs.pop("variables", {})
        import os

        # Get deployment names from variables (playback) or environment (recording)
        # If not found, use defaults and record them
        gpt_4_1_deployment = variables.setdefault("gpt_4_1_deployment", os.getenv("GPT_4_1_DEPLOYMENT", "gpt-4.1"))
        gpt_4_1_mini_deployment = variables.setdefault(
            "gpt_4_1_mini_deployment", os.getenv("GPT_4_1_MINI_DEPLOYMENT", "gpt-4.1-mini")
        )
        text_embedding_3_large_deployment = variables.setdefault(
            "text_embedding_3_large_deployment",
            os.getenv("TEXT_EMBEDDING_3_LARGE_DEPLOYMENT", "text-embedding-3-large"),
        )

        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)

        # Update defaults with configured deployments
        model_deployments = {
            "gpt-4.1": gpt_4_1_deployment,
            "gpt-4.1-mini": gpt_4_1_mini_deployment,
            "text-embedding-3-large": text_embedding_3_large_deployment,
        }

        response = await client.update_defaults(model_deployments=model_deployments)

        assert response is not None, "Update response should not be null"
        assert hasattr(response, "model_deployments"), "Updated defaults should have model_deployments attribute"

        # Verify the updated defaults
        updated_defaults = response
        assert updated_defaults.model_deployments is not None, "Updated model deployments should not be null"
        assert len(updated_defaults.model_deployments) >= 3, "Should have at least 3 model deployments"

        # Verify each deployment was set correctly
        assert "gpt-4.1" in updated_defaults.model_deployments, "Should contain gpt-4.1 deployment"
        assert updated_defaults.model_deployments["gpt-4.1"] == gpt_4_1_deployment, "gpt-4.1 deployment should match"

        assert "gpt-4.1-mini" in updated_defaults.model_deployments, "Should contain gpt-4.1-mini deployment"
        assert (
            updated_defaults.model_deployments["gpt-4.1-mini"] == gpt_4_1_mini_deployment
        ), "gpt-4.1-mini deployment should match"

        assert (
            "text-embedding-3-large" in updated_defaults.model_deployments
        ), "Should contain text-embedding-3-large deployment"
        assert (
            updated_defaults.model_deployments["text-embedding-3-large"] == text_embedding_3_large_deployment
        ), "text-embedding-3-large deployment should match"

        print(f"Successfully updated defaults with {len(updated_defaults.model_deployments)} model deployments")

        # Return variables to be recorded for playback mode
        return variables

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_get_defaults_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests retrieving default model deployments from the Content Understanding service.
        Verifies that the returned defaults contain the expected model deployment configurations.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)

        # Load expected model values from test environment
        gpt_4_1_deployment = os.getenv("GPT_4_1_DEPLOYMENT")
        gpt_4_1_mini_deployment = os.getenv("GPT_4_1_MINI_DEPLOYMENT")
        text_embedding_3_large_deployment = os.getenv("TEXT_EMBEDDING_3_LARGE_DEPLOYMENT")

        response = await client.get_defaults()

        assert response is not None, "Response should not be null"

        # Verify defaults structure
        defaults = response
        assert defaults is not None, "Defaults should not be null"

        # ModelDeployments may be null or empty if not configured
        if defaults.model_deployments is not None and len(defaults.model_deployments) > 0:
            assert len(defaults.model_deployments) > 0, "Model deployments dictionary should not be empty if not null"

            # Verify expected keys exist if deployments are configured
            for key, value in defaults.model_deployments.items():
                assert key is not None and len(key) > 0, "Model deployment key should not be null or empty"
                assert value is not None and len(value) > 0, "Model deployment value should not be null or empty"

            # Verify specific model values if they are configured in test environment
            if gpt_4_1_deployment:
                assert "gpt-4.1" in defaults.model_deployments, "Should contain gpt-4.1 deployment"
                assert (
                    defaults.model_deployments["gpt-4.1"] == gpt_4_1_deployment
                ), "gpt-4.1 deployment should match test environment value"

            if gpt_4_1_mini_deployment:
                assert "gpt-4.1-mini" in defaults.model_deployments, "Should contain gpt-4.1-mini deployment"
                assert (
                    defaults.model_deployments["gpt-4.1-mini"] == gpt_4_1_mini_deployment
                ), "gpt-4.1-mini deployment should match test environment value"

            if text_embedding_3_large_deployment:
                assert (
                    "text-embedding-3-large" in defaults.model_deployments
                ), "Should contain text-embedding-3-large deployment"
                assert (
                    defaults.model_deployments["text-embedding-3-large"] == text_embedding_3_large_deployment
                ), "text-embedding-3-large deployment should match test environment value"

            print(f"Successfully retrieved defaults with {len(defaults.model_deployments)} model deployments")
        else:
            print("Model deployments not configured or empty")

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_create_analyzer_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests creating a custom analyzer using ContentAnalyzer object.
        Verifies analyzer creation, poller properties, and proper cleanup.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "create_content_analyzer", is_async=True)
        created_analyzer = False

        content_analyzer = new_simple_content_analyzer_object(
            analyzer_id=analyzer_id, description=f"test analyzer: {analyzer_id}", tags={"tag1_name": "tag1_value"}
        )

        try:
            # Create analyzer using the refactored function
            poller = await create_analyzer_and_assert_async(client, analyzer_id, content_analyzer)
            created_analyzer = True

        finally:
            # Always clean up the created analyzer, even if the test fails
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_create_analyzer_with_json_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests creating a custom analyzer using JSON dictionary representation.
        Verifies analyzer creation, poller properties, and proper cleanup.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "create_json", is_async=True)
        created_analyzer = False

        try:
            # Create analyzer using the refactored function with JSON resource
            poller = await create_analyzer_and_assert_async(
                client,
                analyzer_id,
                {
                    "analyzerId": analyzer_id,
                    "baseAnalyzerId": "prebuilt-document",
                    "config": {
                        "disableContentFiltering": False,
                        "disableFaceBlurring": False,
                        "enableFace": False,
                        "enableFormula": True,
                        "enableLayout": True,
                        "enableOcr": True,
                        "estimateFieldSourceAndConfidence": True,
                        "returnDetails": True,
                    },
                    "description": f"test analyzer: {analyzer_id}",
                    "fieldSchema": {
                        "fields": {
                            "amount_due": {
                                "description": "Total amount due of this table",
                                "method": "extract",
                                "type": "number",
                            }
                        },
                        "description": "schema description here",
                        "name": "schema name here",
                    },
                    "mode": "standard",
                    "processingLocation": "global",
                    "models": {"completion": "gpt-4.1"},  # Required when using fieldSchema
                    "tags": {"tag1_name": "tag1_value"},
                },
            )
            created_analyzer = True

        finally:
            # Always clean up the created analyzer, even if the test fails
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_update_analyzer_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests updating an analyzer's properties (description and tags).
        Verifies that updates are correctly applied and persisted.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "update", is_async=True)
        created_analyzer = False

        # Create initial analyzer
        initial_analyzer = new_simple_content_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"Initial analyzer for update test: {analyzer_id}",
            tags={"initial_tag": "initial_value"},
        )

        try:
            # Create the initial analyzer using the refactored function
            poller = await create_analyzer_and_assert_async(client, analyzer_id, initial_analyzer)
            created_analyzer = True

            # Get the analyzer before update to verify initial state
            print(f"Getting analyzer {analyzer_id} before update")
            analyzer_before_update = await client.get_analyzer(analyzer_id=analyzer_id)
            assert analyzer_before_update is not None
            assert analyzer_before_update.analyzer_id == analyzer_id
            assert analyzer_before_update.description == f"Initial analyzer for update test: {analyzer_id}"
            assert analyzer_before_update.tags == {"initial_tag": "initial_value"}
            print(
                f"Initial analyzer state verified - description: {analyzer_before_update.description}, tags: {analyzer_before_update.tags}"
            )

            # Create updated analyzer with only allowed properties (description and tags)
            # Note: Service requires baseAnalyzerId and models even in PATCH update
            # This is a service bug - TypeSpec says they should not be required in Update
            updated_analyzer = ContentAnalyzer(
                base_analyzer_id=analyzer_before_update.base_analyzer_id,  # <== SERVICE-FIX: Service will return error without this
                models=analyzer_before_update.models,  # <== SERVICE-FIX: Service will return error without this
                description=f"Updated analyzer for update test: {analyzer_id}",
                tags={"initial_tag": "initial_value", "tag1_field": "updated_value"},
            )

            print(f"Updating analyzer {analyzer_id} with new tag and description")

            # Update the analyzer
            response = await client.update_analyzer(
                analyzer_id=analyzer_id,
                resource=updated_analyzer,
            )

            # Verify the update response
            assert response is not None
            print(f"Update response: {response}")

            # Verify the updated analyzer has the new tag and updated description
            assert response.analyzer_id == analyzer_id
            assert response.tags is not None
            assert "tag1_field" in response.tags
            assert response.tags["tag1_field"] == "updated_value"
            assert response.description == f"Updated analyzer for update test: {analyzer_id}"

            print(f"Successfully updated analyzer {analyzer_id} with new tag and description")

            # Get the analyzer after update to verify the changes persisted
            print(f"Getting analyzer {analyzer_id} after update")
            analyzer_after_update = await client.get_analyzer(analyzer_id=analyzer_id)
            assert analyzer_after_update is not None
            assert analyzer_after_update.analyzer_id == analyzer_id
            assert analyzer_after_update.description == f"Updated analyzer for update test: {analyzer_id}"
            assert analyzer_after_update.tags == {"initial_tag": "initial_value", "tag1_field": "updated_value"}
            print(
                f"Updated analyzer state verified - description: {analyzer_after_update.description}, tags: {analyzer_after_update.tags}"
            )

        finally:
            # Always clean up the created analyzer, even if the test fails
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_get_analyzer_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests retrieving an analyzer by ID.
        Verifies that the prebuilt-documentSearch analyzer can be retrieved with all properties.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        response = await client.get_analyzer(
            analyzer_id="prebuilt-documentSearch",
        )
        assert response is not None
        print(response)
        assert response.analyzer_id == "prebuilt-documentSearch"
        assert response.description is not None
        assert len(response.description) > 0
        assert response.status == "ready"
        assert response.created_at is not None
        assert response.config is not None

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_delete_analyzer_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests deleting an analyzer.
        Verifies that an analyzer can be successfully deleted.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "delete", is_async=True)
        created_analyzer = False

        # Create a simple analyzer for deletion test
        content_analyzer = new_simple_content_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"test analyzer for deletion: {analyzer_id}",
            tags={"test_type": "deletion"},
        )

        try:
            # Create analyzer using the refactored function
            poller = await create_analyzer_and_assert_async(client, analyzer_id, content_analyzer)
            created_analyzer = True

            # Delete the analyzer
            print(f"Deleting analyzer {analyzer_id}")
            response = await client.delete_analyzer(analyzer_id=analyzer_id)

            # Verify the delete response
            assert response is None
            #     client, analyzer_id
            # ), f"Deleted analyzer with ID '{analyzer_id}' was found in the list"
        finally:
            # Clean up if the analyzer was created but deletion failed
            if created_analyzer:
                print(f"Cleaning up analyzer {analyzer_id} that was not properly deleted")
                try:
                    await client.delete_analyzer(analyzer_id=analyzer_id)
                    # Verify deletion (NOTE: check disabled - list too long to execute)
                    #     client, analyzer_id
                    # ), f"Failed to delete analyzer {analyzer_id} during cleanup"
                    print(f"Analyzer {analyzer_id} is deleted successfully during cleanup")
                except Exception as e:
                    # If cleanup fails, the test should fail
                    raise AssertionError(f"Failed to delete analyzer {analyzer_id} during cleanup: {e}") from e
            elif not created_analyzer:
                print(f"Analyzer {analyzer_id} was not created, no cleanup needed")

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_list_analyzers_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests listing all available analyzers.
        Verifies that prebuilt analyzers are included and have required properties.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        response = client.list_analyzers()
        result = [r async for r in response]
        assert len(result) > 0, "Should have at least one analyzer in the list"
        print(f"Found {len(result)} analyzers")
        prebuilt_found = False
        for analyzer in result:
            assert hasattr(analyzer, "analyzer_id"), "Each analyzer should have analyzer_id"
            assert hasattr(analyzer, "description"), "Each analyzer should have description"
            assert hasattr(analyzer, "status"), "Each analyzer should have status"
            assert hasattr(analyzer, "created_at"), "Each analyzer should have created_at"

            if analyzer.analyzer_id == "prebuilt-documentSearch":
                prebuilt_found = True
                assert analyzer.status == "ready", "prebuilt-documentSearch should be ready"
                print(f"Found prebuilt-documentSearch: {analyzer.description}")

        assert prebuilt_found, "prebuilt-documentSearch should be in the list"
        print("List analyzers test completed successfully")

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_analyze_url_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests analyzing a document from a URL.
        Verifies that analysis completes successfully and returns expected field results.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "analyze_url", is_async=True)
        created_analyzer = False

        # Create a simple analyzer for URL analysis
        content_analyzer = new_simple_content_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"test analyzer for URL analysis: {analyzer_id}",
            tags={"test_type": "url_analysis"},
        )

        try:
            # Create analyzer using the refactored function
            poller = await create_analyzer_and_assert_async(client, analyzer_id, content_analyzer)
            created_analyzer = True

            # Use the provided URL for the invoice PDF
            invoice_url = "https://github.com/Azure-Samples/azure-ai-content-understanding-python/raw/refs/heads/main/data/invoice.pdf"

            print(f"Starting URL analysis with analyzer {analyzer_id}")

            # Begin analysis operation with URL
            analysis_poller = await client.begin_analyze(
                analyzer_id=analyzer_id, inputs=[AnalysisInput(url=invoice_url)]
            )
            assert_poller_properties(analysis_poller, "Analysis poller")

            # Wait for analysis completion
            print(f"Waiting for analysis completion")
            analysis_result = await analysis_poller.result()
            print(f"  Analysis completed")

            # Get test file directory for saving output
            test_file_dir = os.path.dirname(os.path.abspath(__file__))
            output_filename = save_analysis_result_to_file(
                analysis_result, "test_content_analyzers_begin_analyze_url", test_file_dir, analyzer_id
            )

            # Now assert the field results
            assert_simple_content_analyzer_result(analysis_result, "Analysis result")

        finally:
            # Always clean up the created analyzer, even if the test fails
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_analyze_binary_basic_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests analyzing a document from binary data (PDF file).
        Verifies that binary analysis completes successfully and returns expected field results.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "analyze_binary", is_async=True)
        created_analyzer = False

        # Create a simple analyzer for binary analysis
        content_analyzer = new_simple_content_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"test analyzer for binary analysis: {analyzer_id}",
            tags={"test_type": "binary_analysis"},
        )

        try:
            # Create analyzer using the refactored function
            poller = await create_analyzer_and_assert_async(client, analyzer_id, content_analyzer)
            created_analyzer = True

            # Read the sample invoice PDF file using absolute path based on this test file's location
            test_file_dir = os.path.dirname(os.path.abspath(__file__))
            pdf_path = os.path.join(test_file_dir, "test_data", "sample_invoice.pdf")
            with open(pdf_path, "rb") as pdf_file:
                pdf_content = pdf_file.read()

            print(f"Starting binary analysis with analyzer {analyzer_id}")

            # Begin binary analysis operation
            analysis_poller = await client.begin_analyze_binary(
                analyzer_id=analyzer_id, binary_input=pdf_content, content_type="application/pdf"
            )
            assert_poller_properties(analysis_poller, "Analysis poller")

            # Wait for analysis completion
            print(f"Waiting for analysis completion")
            analysis_result = await analysis_poller.result()
            print(f"  Analysis completed")

            output_filename = save_analysis_result_to_file(
                analysis_result, "test_content_analyzers_begin_analyze_binary", test_file_dir, analyzer_id
            )

            # Now assert the field results
            assert_simple_content_analyzer_result(analysis_result, "Analysis result")

        finally:
            # Always clean up the created analyzer, even if the test fails
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_get_result_file_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests retrieving result files from a video analysis operation.
        Verifies that image files generated from video analysis can be retrieved and saved.
        """
        if not is_live():
            pytest.skip(
                "This test requires live mode to run, as it involves large video files that are too big for test proxy to record"
            )
            return  # Skip this test in playback mode as it requires large video files is too big for test proxy to record
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "get_result_file", is_async=True)
        created_analyzer = False

        # Create a marketing video analyzer based on the template
        video_analyzer = new_marketing_video_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"marketing video analyzer for get result file test: {analyzer_id}",
            tags={"test_type": "get_result_file_video"},
        )

        try:
            # Create analyzer using the refactored function
            poller = await create_analyzer_and_assert_async(client, analyzer_id, video_analyzer)
            created_analyzer = True

            # Use the FlightSimulator.mp4 video file from remote location
            video_file_url = "https://github.com/Azure-Samples/azure-ai-content-understanding-assets/raw/refs/heads/main/videos/sdk_samples/FlightSimulator.mp4"
            print(f"Using video file from URL: {video_file_url}")

            # Get test file directory for saving output
            test_file_dir = os.path.dirname(os.path.abspath(__file__))

            print(f"Starting video analysis to get operation ID")

            # Begin video analysis operation using URL
            analysis_poller = await client.begin_analyze(
                analyzer_id=analyzer_id, inputs=[AnalysisInput(url=video_file_url)]
            )

            # Wait for analysis completion first
            print(f"Waiting for analysis completion")
            analysis_result = await analysis_poller.result()
            print(f"Analysis completed")

            # Save the analysis result to file
            output_filename = save_analysis_result_to_file(
                analysis_result, "test_content_analyzers_get_result_file", test_file_dir, analyzer_id
            )

            # Extract operation ID for get_result_file test using custom poller's operation_id property
            from azure.ai.contentunderstanding.aio.models import AnalyzeAsyncLROPoller

            assert isinstance(analysis_poller, AnalyzeAsyncLROPoller), "Should return custom AnalyzeAsyncLROPoller"

            analysis_operation_id = analysis_poller.operation_id
            assert analysis_operation_id is not None, "Operation ID should not be None"
            assert len(analysis_operation_id) > 0, "Operation ID should not be empty"
            print(f"Analysis operation ID: {analysis_operation_id}")

            # Use the analysis result we already have from the poller to see what files are available
            result = analysis_result
            assert result is not None, "Analysis result should not be None"
            print(f"Analysis result contains {len(result.contents)} contents")

            # Use the refactored function to download keyframes by calling client.get_result_file
            await download_keyframes_and_assert_async(client, analysis_operation_id, result, test_file_dir, analyzer_id)

        finally:
            # Always clean up the created analyzer, even if the test fails
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_validate_document_properties_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests document property validation from analysis results.
        Verifies that analyzed documents contain expected properties like page count, content structure, and layout information.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "validate_props", is_async=True)
        created_analyzer = False

        # Create a simple analyzer with OCR and layout enabled to get rich document properties
        content_analyzer = new_simple_content_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"test analyzer for document properties validation: {analyzer_id}",
            tags={"test_type": "document_properties"},
        )

        try:
            # Create analyzer
            poller = await create_analyzer_and_assert_async(client, analyzer_id, content_analyzer)
            created_analyzer = True

            # Read the sample invoice PDF file
            test_file_dir = os.path.dirname(os.path.abspath(__file__))
            pdf_path = os.path.join(test_file_dir, "test_data", "sample_invoice.pdf")
            with open(pdf_path, "rb") as pdf_file:
                pdf_content = pdf_file.read()

            print(f"Starting analysis for document properties validation")

            # Begin binary analysis
            analysis_poller = await client.begin_analyze_binary(
                analyzer_id=analyzer_id, binary_input=pdf_content, content_type="application/pdf"
            )
            assert_poller_properties(analysis_poller, "Document properties analysis poller")

            # Wait for completion
            print(f"Waiting for analysis completion")
            analysis_result = await analysis_poller.result()
            print(f"Analysis completed")

            # Save result to file
            output_filename = save_analysis_result_to_file(
                analysis_result, "test_validate_document_properties", test_file_dir, analyzer_id
            )

            # Validate document properties using the new helper function
            # Sample invoice PDF is a single-page document
            assert_document_properties(analysis_result, expected_min_pages=1)

            # Additional specific validations
            assert analysis_result.contents is not None, "Should have contents"
            first_content = analysis_result.contents[0]

            # Verify markdown output exists (basic OCR result)
            assert hasattr(first_content, "markdown"), "Content should have markdown attribute"
            if first_content.markdown:
                assert (
                    len(first_content.markdown) > 100
                ), "Markdown content should contain substantial text from the document"
                print(f"✓ Markdown content length: {len(first_content.markdown)} characters")

            # Verify fields were extracted if field schema was defined
            if hasattr(first_content, "fields") and first_content.fields:
                assert "amount_due" in first_content.fields, "Should extract amount_due field"
                amount_due = first_content.fields["amount_due"]
                assert amount_due is not None, "amount_due field should have a value"
                print(f"✓ Extracted amount_due: {amount_due}")

            print(f"✓ Document properties validation test completed successfully")

        finally:
            # Always clean up the created analyzer
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_analyze_invoice_with_fields_async(self, contentunderstanding_endpoint: str) -> None:
        """
        Tests invoice analysis with comprehensive field extraction.
        Verifies that invoice-specific fields (invoice_number, dates, amounts, vendor/customer info) are correctly extracted.
        This test demonstrates structured data extraction from invoices using field schema.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        analyzer_id = generate_analyzer_id(client, "invoice_fields", is_async=True)
        created_analyzer = False

        # Create an invoice analyzer with comprehensive field schema
        invoice_analyzer = new_invoice_analyzer_object(
            analyzer_id=analyzer_id,
            description=f"test analyzer for invoice field extraction: {analyzer_id}",
            tags={"test_type": "invoice_fields"},
        )

        try:
            # Create analyzer
            print(f"\nCreating invoice analyzer with field schema")
            poller = await create_analyzer_and_assert_async(client, analyzer_id, invoice_analyzer)
            created_analyzer = True

            # Read the sample invoice PDF file
            test_file_dir = os.path.dirname(os.path.abspath(__file__))
            pdf_path = os.path.join(test_file_dir, "test_data", "sample_invoice.pdf")
            with open(pdf_path, "rb") as pdf_file:
                pdf_content = pdf_file.read()

            print(f"Starting invoice analysis with field extraction")

            # Begin binary analysis
            analysis_poller = await client.begin_analyze_binary(
                analyzer_id=analyzer_id, binary_input=pdf_content, content_type="application/pdf"
            )
            assert_poller_properties(analysis_poller, "Invoice analysis poller")

            # Wait for completion
            print(f"Waiting for invoice analysis completion")
            analysis_result = await analysis_poller.result()
            print(f"Invoice analysis completed")

            # Save result to file for inspection
            output_filename = save_analysis_result_to_file(
                analysis_result, "test_analyze_invoice_with_fields", test_file_dir, analyzer_id
            )
            print(f"Analysis result saved to: {output_filename}")

            # Validate invoice fields using the specialized assertion function
            assert_invoice_fields(analysis_result, "Invoice analysis result")

            # Additional validation - verify at least amount_due is extracted (most critical field)
            first_content = analysis_result.contents[0]
            assert hasattr(first_content, "fields"), "Content should have fields"
            assert first_content.fields is not None, "Fields should not be None"

            fields = first_content.fields
            assert "amount_due" in fields, "Should extract amount_due field (most critical invoice field)"

            amount_due_field = fields["amount_due"]
            print(f"\n✓ Critical field verification:")
            print(f"  - amount_due extracted successfully")

            if isinstance(amount_due_field, dict) and "valueNumber" in amount_due_field:
                amount_due_value = amount_due_field["valueNumber"]
                print(f"  - Total amount value: {amount_due_value}")
                assert amount_due_value > 0, "Total amount should be positive"

                # Verify confidence if available
                if "confidence" in amount_due_field:
                    confidence = amount_due_field["confidence"]
                    print(f"  - Confidence: {confidence:.2%}")
                    # Note: We don't enforce a minimum confidence as it depends on document quality

                # Verify source information if available
                if "spans" in amount_due_field:
                    spans = amount_due_field["spans"]
                    print(f"  - Source locations: {len(spans)} span(s)")
                    assert len(spans) > 0, "Should have source location for extracted field"

                if "source" in amount_due_field:
                    source = amount_due_field["source"]
                    print(f"  - Source: {source[:50]}..." if len(source) > 50 else f"  - Source: {source}")

            # Count how many invoice fields were successfully extracted
            invoice_field_names = [
                "invoice_number",
                "invoice_date",
                "due_date",
                "vendor_name",
                "vendor_address",
                "customer_name",
                "customer_address",
                "subtotal",
                "tax_amount",
                "amount_due",
            ]
            extracted_count = sum(1 for field in invoice_field_names if field in fields)
            print(f"\n✓ Successfully extracted {extracted_count}/{len(invoice_field_names)} invoice fields")
            print(f"✓ Invoice field extraction test completed successfully")

        finally:
            # Always clean up the created analyzer
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_analyze_binary_extract_markdown_async(self, contentunderstanding_endpoint: str) -> None:
        """Test extracting markdown content from analyzed binary documents.

        This test corresponds to .NET AnalyzeBinaryAsync_ExtractMarkdown.
        Verifies that markdown is successfully extracted and is non-empty.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)

        print("\n=== Test: Extract Markdown from Binary Document ===")

        # Get test file path
        current_dir = os.path.dirname(os.path.abspath(__file__))
        file_path = os.path.join(current_dir, "test_data", "sample_invoice.pdf")
        assert os.path.exists(file_path), f"Sample file should exist at {file_path}"
        print(f"Test file: {file_path}")

        # Read file content
        with open(file_path, "rb") as f:
            file_bytes = f.read()
        assert len(file_bytes) > 0, "File should not be empty"
        print(f"File size: {len(file_bytes)} bytes")

        # Analyze the document
        print("\nAnalyzing document with prebuilt-documentSearch...")
        poller = await client.begin_analyze_binary(
            analyzer_id="prebuilt-documentSearch",
            binary_input=file_bytes,
            content_type="application/pdf",
        )

        # Wait for completion
        result = await poller.result()
        assert_poller_properties(poller)

        # Verify result
        assert result is not None, "Analysis result should not be null"
        assert hasattr(result, "contents"), "Result should have contents attribute"
        assert result.contents is not None, "Result contents should not be null"
        assert len(result.contents) > 0, "Result should contain at least one content element"
        assert len(result.contents) == 1, "PDF file should have exactly one content element"
        print(f"✓ Analysis completed with {len(result.contents)} content element(s)")

        # Extract markdown from first content
        content = result.contents[0]
        assert content is not None, "Content should not be null"

        # Verify markdown content
        assert hasattr(content, "markdown"), "Content should have markdown attribute"
        assert content.markdown is not None, "Markdown content should not be null"
        assert isinstance(content.markdown, str), "Markdown should be a string"
        assert len(content.markdown) > 0, "Markdown content should not be empty"
        assert content.markdown.strip(), "Markdown content should not be just whitespace"

        print(f"\n✓ Markdown extraction successful:")
        print(f"  - Markdown length: {len(content.markdown)} characters")
        print(f"  - First 100 chars: {content.markdown[:100]}...")
        print(f"✓ Markdown extraction test completed successfully")

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_create_classifier_async(self, contentunderstanding_endpoint: str) -> None:
        """Test creating a classifier with content categories and document segmentation.

        This test corresponds to .NET CreateClassifierAsync.
        Verifies that the classifier is created successfully with the specified categories
        and configuration, and can segment documents into different categories.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)
        created_analyzer = False
        analyzer_id = generate_analyzer_id(client, "test_classifier", is_async=True)

        print(f"\n=== Test: Create Classifier with Segmentation ===")
        print(f"Analyzer ID: {analyzer_id}")

        try:
            # Define content categories for classification
            content_categories = {
                "Loan_Application": {
                    "description": "Documents submitted by individuals or businesses to request funding"
                },
                "Invoice": {
                    "description": "Billing documents issued by sellers or service providers to request payment"
                },
                "Bank_Statement": {
                    "description": "Official statements issued by banks that summarize account activity"
                },
            }

            # Create analyzer configuration with categories and segmentation enabled
            config = {"returnDetails": True, "enableSegment": True, "contentCategories": content_categories}

            # Create the classifier analyzer
            classifier = {
                "baseAnalyzerId": "prebuilt-document",
                "description": "Custom classifier for financial document categorization",
                "config": config,
                "models": {"completion": "gpt-4.1"},
            }

            print(f"\nCreating classifier with {len(content_categories)} categories...")
            print(f"Categories: {', '.join(content_categories.keys())}")

            # Create the classifier
            poller = await create_analyzer_and_assert_async(client, analyzer_id, classifier)
            created_analyzer = True

            # Get the created classifier to verify full details
            get_response = await client.get_analyzer(analyzer_id=analyzer_id)
            assert get_response is not None, "Get analyzer response should not be null"

            result = get_response
            assert result is not None, "Classifier result should not be null"

            # Verify config
            if hasattr(result, "config") and result.config is not None:
                config_dict = result.config if isinstance(result.config, dict) else result.config.as_dict()
                if "contentCategories" in config_dict or "content_categories" in config_dict:
                    categories_key = "contentCategories" if "contentCategories" in config_dict else "content_categories"
                    categories = config_dict[categories_key]
                    assert len(categories) >= 3, "Should have at least 3 content categories"
                    print(f"✓ Classifier created successfully with {len(categories)} categories")
                else:
                    print("  (Config exists but contentCategories not verified - may be service behavior)")
            else:
                print("  (Config verification skipped - result.config is None)")

            print(f"✓ Classifier test completed successfully")

        finally:
            # Always clean up the created analyzer
            await delete_analyzer_and_assert(client, analyzer_id, created_analyzer)

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_analyze_configs_async(self, contentunderstanding_endpoint: str) -> None:
        """Test analyzing a document with specific configurations enabled.

        This test corresponds to .NET AnalyzeConfigsAsync.
        Verifies that document features can be extracted with formulas, layout, and OCR enabled.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)

        print("\n=== Test: Analyze with Specific Configurations ===")

        # Get test file path
        current_dir = os.path.dirname(os.path.abspath(__file__))
        file_path = os.path.join(current_dir, "test_data", "sample_invoice.pdf")

        assert os.path.exists(file_path), f"Test file should exist at {file_path}"
        print(f"Test file: {file_path}")

        # Read file content
        with open(file_path, "rb") as f:
            file_bytes = f.read()
        assert len(file_bytes) > 0, "File should not be empty"
        print(f"File size: {len(file_bytes)} bytes")

        # Analyze with prebuilt-documentSearch which has formulas, layout, and OCR enabled
        print("\nAnalyzing document with prebuilt-documentSearch (formulas, layout, OCR enabled)...")
        poller = await client.begin_analyze_binary(
            analyzer_id="prebuilt-documentSearch",
            binary_input=file_bytes,
            content_type="application/pdf",
        )

        # Wait for completion
        result = await poller.result()
        assert_poller_properties(poller)

        # Verify result
        assert result is not None, "Analysis result should not be null"
        assert hasattr(result, "contents"), "Result should have contents attribute"
        assert result.contents is not None, "Result should contain contents"
        assert len(result.contents) > 0, "Result should have at least one content"
        assert len(result.contents) == 1, "PDF file should have exactly one content element"
        print(f"✓ Analysis completed with {len(result.contents)} content element(s)")

        # Verify document content
        document_content = result.contents[0]
        assert document_content is not None, "Content should not be null"
        assert hasattr(document_content, "start_page_number"), "Should have start_page_number"
        start_page = getattr(document_content, "start_page_number", None)
        assert start_page is not None and start_page >= 1, "Start page should be >= 1"

        if hasattr(document_content, "end_page_number"):
            end_page = getattr(document_content, "end_page_number", None)
            assert end_page is not None and end_page >= start_page, "End page should be >= start page"
            print(f"✓ Document page range: {start_page}-{end_page}")

        # Verify markdown was extracted (OCR/layout result)
        if hasattr(document_content, "markdown") and document_content.markdown:
            print(f"✓ Markdown extracted ({len(document_content.markdown)} characters)")

        print(f"✓ Configuration test completed successfully")

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_analyze_return_raw_json_async(self, contentunderstanding_endpoint: str) -> None:
        """Test analyzing a document and returning raw JSON response.

        This test corresponds to .NET AnalyzeReturnRawJsonAsync.
        Verifies that the raw JSON response can be retrieved and parsed.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)

        print("\n=== Test: Analyze and Return Raw JSON ===")

        # Get test file path
        current_dir = os.path.dirname(os.path.abspath(__file__))
        file_path = os.path.join(current_dir, "test_data", "sample_invoice.pdf")
        assert os.path.exists(file_path), f"Sample file should exist at {file_path}"
        print(f"Test file: {file_path}")

        # Read file content
        with open(file_path, "rb") as f:
            file_bytes = f.read()
        assert len(file_bytes) > 0, "File should not be empty"
        print(f"File size: {len(file_bytes)} bytes")

        # Analyze the document
        print("\nAnalyzing document with prebuilt-documentSearch...")
        poller = await client.begin_analyze_binary(
            analyzer_id="prebuilt-documentSearch",
            binary_input=file_bytes,
            content_type="application/pdf",
        )

        # Wait for completion
        result = await poller.result()
        assert_poller_properties(poller)

        # Verify operation completed successfully
        assert result is not None, "Analysis result should not be null"

        # Verify response can be serialized to JSON
        import json

        result_dict = result.as_dict() if hasattr(result, "as_dict") else dict(result)
        json_str = json.dumps(result_dict, indent=2)
        assert len(json_str) > 0, "JSON string should not be empty"

        # Verify JSON can be parsed back
        parsed = json.loads(json_str)
        assert parsed is not None, "Parsed JSON should not be null"
        assert isinstance(parsed, dict), "Parsed JSON should be a dictionary"

        print(f"✓ JSON serialization successful:")
        print(f"  - JSON length: {len(json_str)} characters")
        print(f"  - Top-level keys: {', '.join(list(parsed.keys())[:5])}...")
        print(f"✓ Raw JSON test completed successfully")

    @ContentUnderstandingPreparer()
    @recorded_by_proxy_async
    async def test_delete_result_async(self, contentunderstanding_endpoint: str) -> None:
        """Test deleting an analysis result.

        This test corresponds to .NET DeleteResultAsync.
        Verifies that an analysis result can be deleted using its operation ID.
        """
        client: ContentUnderstandingClient = self.create_async_client(endpoint=contentunderstanding_endpoint)

        print("\n=== Test: Delete Analysis Result ===")

        # Get test file URI
        document_url = "https://github.com/Azure-Samples/azure-ai-content-understanding-python/raw/refs/heads/main/data/invoice.pdf"
        print(f"Document URL: {document_url}")

        # Start the analysis operation
        print("\nStarting analysis operation...")
        poller = await client.begin_analyze(
            analyzer_id="prebuilt-invoice",
            inputs=[AnalysisInput(url=document_url)],
        )

        # Wait for completion
        print("Waiting for analysis to complete...")
        result = await poller.result()

        # Get the operation ID using the public property
        operation_id = poller.operation_id
        assert operation_id is not None, "Operation ID should not be null"
        assert len(operation_id) > 0, "Operation ID should not be empty"
        print(f"Operation ID: {operation_id}")

        # Verify analysis completed successfully
        assert result is not None, "Analysis result should not be null"
        assert hasattr(result, "contents"), "Result should have contents"
        assert result.contents is not None, "Result should contain contents"
        assert len(result.contents) > 0, "Result should have at least one content"
        print(f"✓ Analysis completed successfully")

        # Delete the analysis result
        print(f"\nDeleting analysis result (operation ID: {operation_id})...")
        await client.delete_result(operation_id=operation_id)

        print(f"✓ Delete result completed successfully")
        print("Note: Deletion success verified by no exception thrown")
        print(f"✓ Delete result test completed successfully")
