import os
import random
import time

import pytest
import requests
from dotenv import load_dotenv

# from esa_client import EsaClient
from src.mcp_esa_server.esa_client import EsaClient

# Load environment variables from .env file
load_dotenv()


@pytest.fixture(scope="module")
def integration_client():
    token = os.getenv("ESA_TOKEN")
    team_name = os.getenv("ESA_TEAM_NAME")
    if not token or not team_name:
        pytest.skip("ESA_TOKEN or ESA_TEAM_NAME not set for integration tests. Skipping integration tests.")
    try:
        client = EsaClient(token=token, team_name=team_name)
        return client
    except ValueError as e:
        pytest.fail(f"Failed to initialize EsaClient for integration tests: {e}. Is .env configured correctly?")
    except Exception as e:
        pytest.fail(f"An unexpected error occurred during EsaClient initialization for integration tests: {e}")


# Note: These tests require a valid .env file with ESA_TOKEN and ESA_TEAM_NAME
# and will make actual API calls to esa.io.
# They might be skipped by default unless explicitly run.


@pytest.mark.integration
def test_get_user_integration(integration_client):
    "Test get_user against the actual esa.io API."
    client = integration_client
    try:
        print(f"\nRunning get_user_integration test for team: {client.team_name}")
        user_data = client.get_user()
        print(f"Received user data: {user_data}")

        # Basic assertions on the structure of the response
        assert isinstance(user_data, dict)
        assert "screen_name" in user_data
        assert "email" in user_data
        print("Integration test passed.")

    except requests.exceptions.RequestException as e:
        pytest.fail(f"Integration test API call failed: {e}")
    except Exception as e:
        pytest.fail(f"Integration test unexpected error: {e}")


@pytest.mark.integration
def test_get_posts_integration(integration_client):
    "Test get_posts against the actual esa.io API (no params)."
    client = integration_client
    try:
        print(f"\nRunning get_posts integration test for team: {client.team_name}")
        posts_data = client.get_posts()
        posts_list = posts_data.get("posts", [])
        print(f"Received {len(posts_list)} posts.")

        # Print title of a random post if available
        if posts_list:
            random_post = random.choice(posts_list)
            print(f"Random post title: {random_post.get('name', 'N/A')}")
        else:
            print("No posts found to display a random title.")

        # Basic assertions on the structure
        assert isinstance(posts_data, dict)
        assert "posts" in posts_data
        assert isinstance(posts_data["posts"], list)

        # Optionally, check structure of the first post if it exists
        if posts_data["posts"]:
            assert isinstance(posts_data["posts"][0], dict)
            assert "number" in posts_data["posts"][0]
            assert "name" in posts_data["posts"][0]

        print("get_posts integration test passed.")

    except ValueError as e:
        pytest.fail(f"Integration test setup failed: {e}. Is .env configured correctly?")
    except requests.exceptions.RequestException as e:
        pytest.fail(f"Integration test API call failed: {e}")
    except Exception as e:
        pytest.fail(f"Integration test unexpected error: {e}")


@pytest.mark.integration
def test_get_posts_pagination_integration(integration_client):
    "Test get_posts pagination against the actual esa.io API."
    client = integration_client
    test_page = 2
    test_per_page = 5
    try:
        print(f"\nRunning get_posts pagination integration test (page={test_page}, per_page={test_per_page})")
        posts_data = client.get_posts(page=test_page, per_page=test_per_page)
        posts_list = posts_data.get("posts", [])
        received_count = len(posts_list)
        print(f"Received {received_count} posts.")
        print(
            f"Response metadata: page={posts_data.get('page')}, "
            f"per_page={posts_data.get('per_page')}, "
            f"total_count={posts_data.get('total_count')}"
        )

        # Assertions on response structure and metadata
        assert isinstance(posts_data, dict)
        assert "posts" in posts_data
        assert isinstance(posts_list, list)
        assert posts_data.get("page") == test_page
        assert posts_data.get("per_page") == test_per_page
        # Check received count vs per_page, considering the last page case
        total_count = posts_data.get("total_count")
        if total_count is not None and total_count > (test_page - 1) * test_per_page:
            assert received_count <= test_per_page
            if total_count >= test_page * test_per_page:
                assert received_count == test_per_page  # Should receive full page if not last
        else:
            assert received_count == 0  # Should receive 0 if page is beyond total count

        print("get_posts pagination integration test passed.")

    except ValueError as e:
        pytest.fail(f"Integration test setup failed: {e}. Is .env configured correctly?")
    except requests.exceptions.RequestException as e:
        pytest.fail(f"Integration test API call failed: {e}")
    except Exception as e:
        pytest.fail(f"Integration test unexpected error: {e}")


@pytest.mark.integration
def test_get_post_integration(integration_client):
    "Test get_post against the actual esa.io API (existing post)."
    client = integration_client
    # Note: Assumes post number 1 exists in the test team.
    # Adjust post_number if necessary for your test environment.
    test_post_number = 1
    try:
        print(f"\nRunning get_post integration test for post #{test_post_number}")
        post_data = client.get_post(post_number=test_post_number)
        print(f"Received post title: {post_data.get('name')}")

        # Assertions on response structure and content
        assert isinstance(post_data, dict)
        assert post_data.get("number") == test_post_number
        assert "name" in post_data
        assert "body_md" in post_data

        print("get_post integration test passed.")

    except ValueError as e:
        pytest.fail(f"Integration test setup failed: {e}. Is .env configured correctly?")
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 404:
            pytest.fail(f"Post #{test_post_number} not found. Please use an existing post number for the test.")
        else:
            pytest.fail(f"Integration test API call failed: {e}")
    except Exception as e:
        pytest.fail(f"Integration test unexpected error: {e}")


@pytest.mark.integration
def test_get_post_not_found_integration(integration_client):
    "Test get_post against the actual esa.io API (non-existent post)."
    client = integration_client
    # Use a post number that is guaranteed not to exist (e.g., 0 or a very large number)
    test_post_number = 0
    try:
        print(f"\nRunning get_post integration test for non-existent post #{test_post_number}")
        with pytest.raises(requests.exceptions.HTTPError) as exc_info:
            client.get_post(post_number=test_post_number)

        # Assert that the error is a 404 Not Found
        assert exc_info.value.response.status_code == 404
        print("get_post 404 integration test passed.")

    except ValueError as e:
        pytest.fail(f"Integration test setup failed: {e}. Is .env configured correctly?")
    except requests.exceptions.RequestException as e:
        # If it's not an HTTPError or not 404, it's an unexpected failure
        if not isinstance(e, requests.exceptions.HTTPError) or e.response.status_code != 404:
            pytest.fail(f"Integration test API call failed unexpectedly: {e}")
        # If it IS a 404 HTTPError, the test should have passed inside the 'with' block.
        # This part should ideally not be reached if the exception is caught correctly.
    except Exception as e:
        pytest.fail(f"Integration test unexpected error: {e}")


@pytest.mark.integration
def test_create_post_integration(integration_client):
    """Test create_post and verify the created post."""
    client = integration_client
    unique_name = f"Test Post - {int(time.time())}"
    post_payload = {
        "name": unique_name,
        "body_md": "This post is created to be verified by an integration test.",
        "tags": ["test", "integration"],
        "category": "integration_tests/created",
        "wip": True,
    }

    created_post_data = None
    try:
        # 1. Create a post
        created_post_data = client.create_post(payload=post_payload)
        assert created_post_data is not None
        assert "number" in created_post_data
        post_number = created_post_data["number"]
        print(f"Successfully created post for verification test: #{post_number} - {created_post_data.get('url')}")

        # 2. Verify the post is created (attempting to get it should result in 200)
        get_post_data = client.get_post(post_number=post_number)
        assert get_post_data is not None
        assert get_post_data.get("number") == post_number
        assert get_post_data.get("name") == unique_name
        print(f"Verified post #{post_number} exists and matches the created post.")

    except Exception as e:
        if created_post_data and created_post_data.get("number"):
            post_number_to_clean = created_post_data["number"]
            try:
                print(
                    f"Test failed, attempting to clean up post #{post_number_to_clean} "
                    f"from test_create_post_integration."
                )
                client.delete_post(post_number_to_clean)
                print(f"Cleaned up post #{post_number_to_clean} after test failure.")
            except Exception as cleanup_e:
                print(f"Failed to clean up post #{post_number_to_clean} after test failure: {cleanup_e}")
        raise e
    finally:
        if created_post_data and created_post_data.get("number"):
            post_number_to_delete = created_post_data["number"]
            try:
                print(f"Attempting to clean up post #{post_number_to_delete} created by test_create_post_integration.")
                client.delete_post(post_number_to_delete)
                print(f"Successfully cleaned up post #{post_number_to_delete}.")
            except requests.exceptions.HTTPError as e:
                print(
                    f"Failed to clean up post #{post_number_to_delete} "
                    f"during test_create_post_integration: {e}. "
                    f"It might have been already deleted."
                )
            except Exception as e:
                print(f"An unexpected error occurred during cleanup of post #{post_number_to_delete}: {e}")
        pass


@pytest.mark.integration
def test_delete_post_integration(integration_client):
    """Test create_post, then delete_post, and verify deletion."""
    client = integration_client
    unique_name = f"Test Post for Deletion - {int(time.time())}"
    post_payload = {
        "name": unique_name,
        "body_md": "This post is created to be deleted by an integration test.",
        "tags": ["test", "delete"],
        "category": "integration_tests/to_be_deleted",
        "wip": True,
    }

    created_post_data = None
    try:
        created_post_data = client.create_post(payload=post_payload)
        assert created_post_data is not None
        assert "number" in created_post_data
        post_number = created_post_data["number"]
        print(f"Successfully created post for deletion test: #{post_number} - {created_post_data.get('url')}")

        client.delete_post(post_number)
        print(f"Successfully called delete_post for #{post_number}")

        with pytest.raises(requests.exceptions.HTTPError) as excinfo:
            client.get_post(post_number)
        assert excinfo.value.response.status_code == 404
        print(f"Verified post #{post_number} is no longer accessible (404 received).")

    except Exception as e:
        if created_post_data and created_post_data.get("number"):
            post_number_to_clean = created_post_data["number"]
            try:
                print(
                    f"Test failed, attempting to clean up post #{post_number_to_clean} "
                    f"from test_delete_post_integration."
                )
                client.delete_post(post_number_to_clean)
                print(f"Cleaned up post #{post_number_to_clean} after test failure.")
            except Exception as cleanup_e:
                print(f"Failed to clean up post #{post_number_to_clean} after test failure: {cleanup_e}")
        raise e


@pytest.mark.integration
def test_delete_post_non_existent_integration(integration_client):
    """Test delete_post with a non-existent post_number."""
    client = integration_client
    non_existent_post_number = 0
    with pytest.raises(requests.exceptions.HTTPError) as excinfo:
        client.delete_post(non_existent_post_number)
    assert excinfo.value.response.status_code == 404
    print(f"Verified deleting non-existent post #{non_existent_post_number} results in 404.")


@pytest.mark.integration
def test_update_post_integration(integration_client):
    """Test creating, updating, and verifying a post."""
    client = integration_client
    original_name = f"Original Post Title - {int(time.time())}"
    updated_name = f"Updated Post Title - {int(time.time())}"
    post_payload = {
        "name": original_name,
        "body_md": "This is the original body of the post.",
        "tags": ["original", "test"],
        "category": "integration_tests/original_category",
        "wip": True,
    }

    created_post_data = None
    try:
        # 1. Create an initial post
        created_post_data = client.create_post(payload=post_payload)
        assert created_post_data is not None
        post_number = created_post_data["number"]
        print(f"Successfully created post #{post_number} for update test: {created_post_data.get('url')}")

        # 2. Update the post
        update_payload = {
            "name": updated_name,
            "body_md": "This is the updated body.",
            "tags": ["updated", "test", "integration"],
            "category": "integration_tests/updated_category",
            "wip": False,  # Change WIP status as well
            "message": "Updating post via integration test",
        }
        updated_post_data = client.update_post(post_number, payload=update_payload)
        assert updated_post_data is not None
        assert updated_post_data["number"] == post_number
        assert updated_post_data["name"] == updated_name
        assert updated_post_data["wip"] is False  # Verify WIP status change
        print(f"Successfully updated post #{post_number}. New URL: {updated_post_data.get('url')}")

        # 3. Verify the updated post by fetching it again
        fetched_post_data = client.get_post(post_number)
        assert fetched_post_data["name"] == updated_name
        assert fetched_post_data["body_md"] == update_payload["body_md"]
        assert sorted(fetched_post_data["tags"]) == sorted(update_payload["tags"])
        assert fetched_post_data["category"] == update_payload["category"]
        assert fetched_post_data["wip"] == update_payload["wip"]
        print(f"Successfully verified updated content for post #{post_number}.")

    finally:
        # Cleanup: Delete the post if it was created
        if created_post_data and created_post_data.get("number"):
            post_number_to_delete = created_post_data["number"]
            try:
                print(f"Attempting to clean up post #{post_number_to_delete} from test_update_post_integration.")
                client.delete_post(post_number_to_delete)
                print(f"Successfully cleaned up post #{post_number_to_delete}.")
            except Exception as e:
                print(f"Failed to clean up post #{post_number_to_delete} during test_update_post_integration: {e}")


@pytest.mark.integration
def test_update_post_not_found_integration(integration_client):
    """Test updating a non-existent post."""
    client = integration_client
    non_existent_post_number = 0  # A post number that is guaranteed not to exist
    update_payload = {"name": "Attempt to update non-existent post", "body_md": "This should not succeed."}

    with pytest.raises(requests.exceptions.HTTPError) as excinfo:
        client.update_post(non_existent_post_number, payload=update_payload)

    assert excinfo.value.response.status_code == 404
    print(f"Verified updating non-existent post #{non_existent_post_number} results in 404.")


@pytest.mark.integration
def test_create_post_with_all_fields_integration(integration_client):
    """Test create_post with all optional fields and verify them."""
    client = integration_client
    unique_suffix = int(time.time())
    post_name = f"Test All Fields - {unique_suffix}"
    post_body = f"Body for all fields test created at {unique_suffix}."
    post_tags = [f"tag1-{unique_suffix}", f"tag2-{unique_suffix}", "all-fields-test"]
    post_category = f"integration_tests/all_fields_test_category/{unique_suffix}"
    post_wip = False  # Test with a specific WIP status
    post_message = f"Commit message for all fields test - {unique_suffix}"

    post_payload = {
        "name": post_name,
        "body_md": post_body,
        "tags": post_tags,
        "category": post_category,
        "wip": post_wip,
        "message": post_message,
    }

    created_post_data = None
    try:
        # 1. Create a post with all specified fields
        created_post_data = client.create_post(payload=post_payload)
        assert created_post_data is not None
        post_number = created_post_data["number"]
        print(f"Successfully created post #{post_number} for all_fields test: {created_post_data.get('url')}")

        # 2. Assertions on the creation response (fields that are usually in create response)
        assert created_post_data.get("name") == post_name
        # Tags might be empty in create response if not processed immediately, check fetched one later.
        # Category might be different if it's a new category path, check fetched one.
        assert created_post_data.get("wip") == post_wip
        assert created_post_data.get("message") == post_message

        # 3. Verify the post by fetching it to check all fields accurately
        fetched_post_data = client.get_post(post_number=post_number)
        assert fetched_post_data is not None
        assert fetched_post_data.get("number") == post_number
        assert fetched_post_data.get("name") == post_name
        assert fetched_post_data.get("body_md") == post_body
        assert sorted(fetched_post_data.get("tags", [])) == sorted(post_tags)
        assert fetched_post_data.get("category") == post_category
        assert fetched_post_data.get("wip") == post_wip
        # The commit message is part of the revision, not directly on the fetched post data usually.
        # The check on created_post_data['message'] is the primary one for the commit message.
        print(f"Successfully verified created post #{post_number} with all fields.")

    finally:
        # Cleanup: Delete the post if it was created
        if created_post_data and created_post_data.get("number"):
            post_number_to_delete = created_post_data["number"]
            try:
                print(
                    f"Attempting to clean up post #{post_number_to_delete} from "
                    f"test_create_post_with_all_fields_integration."
                )
                client.delete_post(post_number_to_delete)
                print(f"Successfully cleaned up post #{post_number_to_delete}.")
            except Exception as e:
                print(
                    f"Failed to clean up post #{post_number_to_delete} during "
                    f"test_create_post_with_all_fields_integration: {e}"
                )
