"""
Tests for staff e-signature upload, transparency normalization, and removal.
"""

from io import BytesIO

import pytest
import pytest_asyncio
from httpx import AsyncClient
from PIL import Image
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.security import create_access_token, hash_password
from app.models.lab import Lab
from app.models.user import User, UserRole
from app.repositories.permission_repository import PermissionRepository
from app.services.signature_service import read_signature_bytes, whiten_to_transparent


def _sig_png() -> bytes:
    img = Image.new("RGB", (200, 80), "white")
    for x in range(40, 160):
        for y in range(30, 50):
            img.putpixel((x, y), (10, 10, 10))
    buf = BytesIO()
    img.save(buf, format="PNG")
    return buf.getvalue()


@pytest_asyncio.fixture(autouse=True)
async def seed_perms(db_session: AsyncSession) -> None:
    """Seed system permissions for RBAC enforcement."""
    repo = PermissionRepository()
    await repo.seed_permissions(db_session)


@pytest_asyncio.fixture
async def sig_lab(db_session: AsyncSession) -> Lab:
    """Create primary test branch lab."""
    lab = Lab(name="Signature Lab", address="Kathmandu")
    db_session.add(lab)
    await db_session.commit()
    await db_session.refresh(lab)
    return lab


@pytest_asyncio.fixture
async def pathologist_token(db_session: AsyncSession, sig_lab: Lab) -> str:
    """Create pathologist user token (self-service signature, no permission needed)."""
    user = User(
        lab_id=sig_lab.id,
        name="Sign Pathologist",
        phone="9819100001",
        role=UserRole.PATHOLOGIST,
        hashed_password=hash_password("PathPass123!"),
        is_active=True,
    )
    db_session.add(user)
    await db_session.commit()
    await db_session.refresh(user)

    return create_access_token(
        subject=str(user.id),
        extra_claims={
            "phone": user.phone,
            "role": user.role.value,
            "lab_id": str(sig_lab.id),
        },
    )


def test_whiten_to_transparent_keeps_ink_drops_paper() -> None:
    img = Image.new("RGB", (10, 10), "white")
    img.putpixel((5, 5), (0, 0, 0))
    out = whiten_to_transparent(img)
    assert out.mode == "RGBA"
    assert out.getpixel((0, 0))[3] == 0
    assert out.getpixel((5, 5))[3] > 0


def test_read_signature_bytes_rejects_path_traversal() -> None:
    assert read_signature_bytes(None) is None
    assert read_signature_bytes("../../etc/passwd") is None


@pytest.mark.asyncio
class TestSignatureEndpoints:
    """Upload → download → delete lifecycle on /users/me/signature."""

    async def test_upload_download_delete_roundtrip(
        self, client: AsyncClient, pathologist_token: str
    ) -> None:
        headers = {"Authorization": f"Bearer {pathologist_token}"}

        up = await client.post(
            "/api/v1/users/me/signature",
            files={"file": ("sig.png", _sig_png(), "image/png")},
            headers=headers,
        )
        assert up.status_code == 200, up.text
        assert up.json()["data"]["signature_image_path"] is not None

        down = await client.get("/api/v1/users/me/signature", headers=headers)
        assert down.status_code == 200
        assert down.headers["content-type"] == "image/png"
        stored = Image.open(BytesIO(down.content))
        assert stored.mode == "RGBA"

        delete = await client.request("DELETE", "/api/v1/users/me/signature", headers=headers)
        assert delete.status_code == 200, delete.text
        assert delete.json()["data"]["signature_image_path"] is None

        gone = await client.get("/api/v1/users/me/signature", headers=headers)
        assert gone.status_code == 404

    async def test_reject_non_image(self, client: AsyncClient, pathologist_token: str) -> None:
        headers = {"Authorization": f"Bearer {pathologist_token}"}
        res = await client.post(
            "/api/v1/users/me/signature",
            files={"file": ("sig.txt", b"not an image", "text/plain")},
            headers=headers,
        )
        assert res.status_code == 422, res.text
