import os
import uuid
import pytest
import asyncio

import polarion_rest_client as prc
from polarion_rest_client.client import PolarionClient, PolarionAsyncClient
from polarion_rest_client.document_attachment import DocumentAttachment


# --- Synchronous Implementation ---
def _run_attachment_test_sync(api, project_id, space_id, document_name, allow_create):
    """Run Document Attachment tests in sync mode."""
    # -------- LIST (always available) ----------
    print(f"Listing attachments for {project_id}/{space_id}/{document_name}")
    attachments = api.list(project_id, space_id, document_name, page_size=10)
    print(f"  Found {len(attachments)} attachments")

    if not allow_create:
        # GET-only mode: just list and optionally get first attachment
        if attachments:
            first_id = attachments[0].get("id", "").split("/")[-1]
            if first_id:
                got = api.get(project_id, space_id, document_name, first_id)
                assert (got.get("data") or {}).get("id", "").endswith(f"/{first_id}")
        return

    # -------- CREATE + GET + UPDATE + GET_CONTENT ---
    token = uuid.uuid4().hex[:6]
    file_name = f"test-attachment-sync-{token}.txt"
    file_content = f"Test content created by sync tests ({token})".encode("utf-8")

    print(f"Creating attachment: {file_name}")
    created = api.create(
        project_id,
        space_id,
        document_name,
        file_data=file_content,
        file_name=file_name,
        mime_type="text/plain",
    )

    created_id = created.get("id", "")
    print(f"  Created: {created_id}")
    assert file_name in created_id or created_id

    # Extract attachment ID from full ID (format: project/space/doc/attachmentId)
    attachment_id = created_id.split("/")[-1] if "/" in created_id else created_id

    # GET the created attachment
    print(f"Fetching attachment: {attachment_id}")
    got = api.get(project_id, space_id, document_name, attachment_id, fields_attachments=["@all"])
    got_id = (got.get("data") or {}).get("id", "")
    assert got_id.endswith(f"/{attachment_id}") or attachment_id in got_id

    # GET CONTENT
    print("Fetching attachment content...")
    content = api.get_content(project_id, space_id, document_name, attachment_id)
    assert isinstance(content, bytes)
    assert len(content) > 0
    print(f"  Content length: {len(content)} bytes")

    # UPDATE title
    new_title = f"Test Attachment Sync {token} (updated)"
    print(f"Updating attachment title to: {new_title}")
    api.update(project_id, space_id, document_name, attachment_id, title=new_title)

    # Verify update
    got2 = api.get(project_id, space_id, document_name, attachment_id, fields_attachments=["@all"])
    attrs = ((got2.get("data") or {}).get("attributes") or {})
    if "title" in attrs:
        assert attrs["title"] == new_title


# --- Asynchronous Implementation ---
async def _run_attachment_test_async(api, project_id, space_id, document_name, allow_create):
    """Run Document Attachment tests in async mode."""
    # -------- LIST (always available) ----------
    print(f"Listing attachments for {project_id}/{space_id}/{document_name}")
    attachments = await api.list(project_id, space_id, document_name, page_size=10)
    print(f"  Found {len(attachments)} attachments")

    if not allow_create:
        # GET-only mode: just list and optionally get first attachment
        if attachments:
            first_id = attachments[0].get("id", "").split("/")[-1]
            if first_id:
                got = await api.get(project_id, space_id, document_name, first_id)
                assert (got.get("data") or {}).get("id", "").endswith(f"/{first_id}")
        return

    # -------- CREATE + GET + UPDATE + GET_CONTENT ---
    token = uuid.uuid4().hex[:6]
    file_name = f"test-attachment-async-{token}.txt"
    file_content = f"Test content created by async tests ({token})".encode("utf-8")

    print(f"Creating attachment: {file_name}")
    created = await api.create(
        project_id,
        space_id,
        document_name,
        file_data=file_content,
        file_name=file_name,
        mime_type="text/plain",
    )

    created_id = created.get("id", "")
    print(f"  Created: {created_id}")
    assert file_name in created_id or created_id

    attachment_id = created_id.split("/")[-1] if "/" in created_id else created_id

    # GET
    print(f"Fetching attachment: {attachment_id}")
    got = await api.get(project_id, space_id, document_name, attachment_id, fields_attachments=["@all"])
    got_id = (got.get("data") or {}).get("id", "")
    assert got_id.endswith(f"/{attachment_id}") or attachment_id in got_id

    # GET CONTENT
    print("Fetching attachment content...")
    content = await api.get_content(project_id, space_id, document_name, attachment_id)
    assert isinstance(content, bytes)
    assert len(content) > 0
    print(f"  Content length: {len(content)} bytes")

    # UPDATE
    new_title = f"Test Attachment Async {token} (updated)"
    print(f"Updating attachment title to: {new_title}")
    await api.update(project_id, space_id, document_name, attachment_id, title=new_title)

    # Verify
    got2 = await api.get(project_id, space_id, document_name, attachment_id, fields_attachments=["@all"])
    attrs = ((got2.get("data") or {}).get("attributes") or {})
    if "title" in attrs:
        assert attrs["title"] == new_title


# --- Test Entry Point ---
@pytest.mark.integration
@pytest.mark.parametrize("mode", ["sync", "async"])
def test_document_attachment_crud_or_list_only(mode):
    """
    Runs Document Attachment CRUD or LIST-only test in either sync or async mode.
    """
    project_id = os.getenv("POLARION_TEST_PROJECT_ID")
    space_id = os.getenv("POLARION_TEST_SPACE_ID") or "_default"
    document_name = os.getenv("POLARION_TEST_DOCUMENT_NAME")

    if not project_id:
        pytest.skip("Set POLARION_TEST_PROJECT_ID to run Document Attachment tests.")
    if not document_name:
        pytest.skip("Set POLARION_TEST_DOCUMENT_NAME to run Document Attachment tests.")

    allow_create = os.getenv("POLARION_TEST_DOC_CREATE_OK") == "1"

    if mode == "sync":
        try:
            pc = PolarionClient(**prc.get_env_vars())
        except Exception:
            pytest.skip("Polarion env vars not configured")
            return

        api = DocumentAttachment(pc)
        _run_attachment_test_sync(api, project_id, space_id, document_name, allow_create)

    else:
        # Async Mode
        async def _async_wrapper():
            try:
                pc_async = PolarionAsyncClient(**prc.get_env_vars())
            except Exception:
                pytest.skip("Polarion env vars not configured")
                return
            except RuntimeError as e:
                if "AuthenticatedAsyncClient" in str(e) or "not available" in str(e):
                    pytest.skip(f"Async support missing in generated client: {e}")
                raise e

            async with pc_async as client:
                api = DocumentAttachment(client)
                await _run_attachment_test_async(api, project_id, space_id, document_name, allow_create)

        asyncio.run(_async_wrapper())


@pytest.mark.integration
def test_document_attachment_iter(polarion_test_client):
    """Test iterating over document attachments."""
    project_id = os.getenv("POLARION_TEST_PROJECT_ID")
    space_id = os.getenv("POLARION_TEST_SPACE_ID") or "_default"
    document_name = os.getenv("POLARION_TEST_DOCUMENT_NAME")

    if not project_id or not document_name:
        pytest.skip("Set POLARION_TEST_PROJECT_ID and POLARION_TEST_DOCUMENT_NAME")

    api = DocumentAttachment(polarion_test_client)

    count = 0
    for attachment in api.iter(project_id, space_id, document_name, page_size=10):
        assert "id" in attachment or "type" in attachment
        count += 1
        if count >= 5:  # Limit iteration for test
            break

    print(f"Iterated over {count} attachments")
