import uuid

import pytest
from httpx import AsyncClient
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models import ContactSubmission


BASE_PAYLOAD = {
    "first_name": "Alice",
    "last_name": "Smith",
    "email": "alice.smith@acme-corp.example",
    "company_name": "Acme Corp",
    "country": "US",
    "job_title": "Security Analyst",
    "inquiry_type": "Data-as-a-Service",
    "message": "We need help reviewing our security architecture.",
    "privacy_policy_agreement": True,
    "captcha_token": "valid-token",
}


@pytest.mark.asyncio
async def test_successful_contact_submission(monkeypatch, client: AsyncClient, db_session: AsyncSession):
    """Submit a valid payload and confirm the contact is persisted."""

    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    response = await client.post("/api/v1/contact", json=BASE_PAYLOAD)
    assert response.status_code == 201, response.text
    body = response.json()
    assert "id" in body, "Response must include the created submission ID."
    assert body["message"] == "Submission received"

    query = select(ContactSubmission).where(ContactSubmission.id == uuid.UUID(body["id"]))
    result = await db_session.execute(query)
    submission = result.scalar_one_or_none()
    assert submission is not None, "Submission should be saved in the database."
    assert submission.first_name == "Alice"
    assert submission.email == "alice.smith@acme-corp.example"
    assert submission.ip_address != "127.0.0.1"
    assert len(submission.ip_address) == 64
    assert submission.created_at is not None


@pytest.mark.asyncio
async def test_missing_required_fields(monkeypatch, client: AsyncClient):
    """Missing required fields should return HTTP 422."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    payload = BASE_PAYLOAD.copy()
    payload.pop("first_name")
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422
    assert response.json()["detail"], "Validation errors must be returned."


@pytest.mark.asyncio
async def test_invalid_inquiry_type(monkeypatch, client: AsyncClient):
    """Invalid inquiry_type should fail validation."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    payload = BASE_PAYLOAD.copy()
    payload["inquiry_type"] = "Hacking Services"
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422
    assert response.json()["detail"], "Invalid enum values must be rejected."


@pytest.mark.asyncio
async def test_length_constraints(monkeypatch, client: AsyncClient):
    """Enforce min/max length constraints on form fields."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    payload = BASE_PAYLOAD.copy()
    payload["first_name"] = "A" * 51
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422

    payload = BASE_PAYLOAD.copy()
    payload["message"] = "Too short"
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422

    payload = BASE_PAYLOAD.copy()
    payload["message"] = "A" * 2001
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422


@pytest.mark.asyncio
async def test_invalid_email(monkeypatch, client: AsyncClient):
    """Malformed email addresses should fail validation."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    payload = BASE_PAYLOAD.copy()
    payload["email"] = "not-an-email"
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422
    assert response.json()["detail"], "Invalid email addresses must be rejected."


@pytest.mark.asyncio
async def test_privacy_policy_unchecked(monkeypatch, client: AsyncClient):
    """Submitting without privacy agreement should return a validation error."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    payload = BASE_PAYLOAD.copy()
    payload["privacy_policy_agreement"] = False
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 422
    assert response.json()["detail"], "Privacy agreement must be enforced."


@pytest.mark.asyncio
async def test_xss_input_accepted_and_stored(monkeypatch, client: AsyncClient, db_session: AsyncSession):
    """Ensure text input containing HTML is safely accepted and stored as raw text."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    payload = BASE_PAYLOAD.copy()
    payload["first_name"] = "<script>alert('xss')</script>"
    payload["message"] = "<strong>Testing</strong> message body."
    response = await client.post("/api/v1/contact", json=payload)
    assert response.status_code == 201

    query = select(ContactSubmission).where(ContactSubmission.first_name == payload["first_name"])
    result = await db_session.execute(query)
    submission = result.scalar_one_or_none()
    assert submission is not None
    assert submission.first_name == payload["first_name"]
    assert submission.message == payload["message"]


@pytest.mark.asyncio
async def test_invalid_captcha(monkeypatch, client: AsyncClient):
    """A failed captcha validation should return a 400 response."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return False

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    response = await client.post("/api/v1/contact", json=BASE_PAYLOAD)
    assert response.status_code == 400
    assert response.json()["detail"] == "Captcha verification failed."


@pytest.mark.asyncio
async def test_rate_limit_exceeded(monkeypatch, client: AsyncClient):
    """More than three submissions from the same IP should trigger rate limiting."""
    async def mock_verify_captcha(token: str, remote_ip: str | None = None) -> bool:
        return True

    from app import captcha

    monkeypatch.setattr(captcha, "verify_captcha", mock_verify_captcha)

    for _ in range(3):
        response = await client.post("/api/v1/contact", json=BASE_PAYLOAD)
        assert response.status_code == 201

    response = await client.post("/api/v1/contact", json=BASE_PAYLOAD)
    assert response.status_code == 429
    assert response.json()["detail"] == "Too many requests. Please try again later."
