from unittest.mock import patch

import pytest
import pytest_asyncio
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from starlette.testclient import TestClient

from src.database import Base, get_db
from src.manufacturer.models import Manufacturer
from src.manufacturer.schemas import ManufacturerCreateForm
from src.manufacturer.service import ManufacturerService
from src.main import app

DATABASE_URL = "sqlite+aiosqlite:///:memory:"

engine_test = create_async_engine(DATABASE_URL, future=True, echo=False)
AsyncSessionLocal = sessionmaker(bind=engine_test, expire_on_commit=False, class_=AsyncSession)


@pytest_asyncio.fixture(scope="session")
async def db_engine():
    async with engine_test.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield engine_test


@pytest_asyncio.fixture(scope="function")
async def db_session(db_engine):
    async with AsyncSessionLocal() as session:
        yield session
        await session.rollback()


@pytest.fixture(scope="function")
def client(db_session):
    def override_get_db():
        yield db_session

    app.dependency_overrides[get_db] = override_get_db

    with TestClient(app) as client:
        yield client

    app.dependency_overrides.clear()


@pytest_asyncio.fixture()
async def prepare_manufacturer(db_session):
    manufacturer = Manufacturer(name="Prepared Manufacturer")
    db_session.add(manufacturer)
    await db_session.commit()
    await db_session.refresh(manufacturer)
    return manufacturer


# --- API TESTS (синхронні, бо TestClient синхронний) ---

def test_create_manufacturer_success(client):
    data = {
        "name": "Test Manufacturer",
        "seo_keyword": "test-keyword",
        "meta_title": "Meta Title",
        "meta_description": "Some description",
        "meta_keyword": "keyword",
    }

    response = client.post(
        "/manufacturer",
        data=data,
        files={"logo": ("test.png", b"fake-image-content", "image/png")},
    )

    assert response.status_code == 201
    res_json = response.json()
    assert res_json["status"] is True
    assert res_json["data"]["name"] == "Test Manufacturer"


def test_get_manufacturer_by_id(client, prepare_manufacturer):
    manufacturer = prepare_manufacturer  # НЕ await!
    response = client.get(f"/manufacturer/{manufacturer.manufacturer_id}")
    assert response.status_code == 200
    assert response.json()["data"]["name"] == manufacturer.name


def test_get_manufacturer_not_found(client):
    response = client.get("/manufacturer/999999")
    assert response.status_code == 200
    assert response.json()["status"] is False


def test_list_manufacturers(client):
    response = client.get("/manufacturer?page=1&limit=5")
    assert response.status_code == 200
    data = response.json()["data"]
    assert "items" in data and "total" in data


def test_list_manufacturers_sort_error(client):
    response = client.get("/manufacturer?sort[field]=unknown")
    assert response.status_code == 200
    assert response.json()["status"] is False
    assert "Invalid sort field" in response.json()["message"]



# def test_update_manufacturer_success(client, prepare_manufacturer):
#     manufacturer = prepare_manufacturer
#     response = client.patch(
#         f"/manufacturer/{manufacturer.manufacturer_id}",
#         data={"name": "Updated Name"},  # Передаємо як form field
#     )
#     assert response.status_code == 200
#     assert response.json()["status"] is True
#     assert response.json()["data"]["name"] == "Updated Name"
#
#
# def test_update_manufacturer_logo(client, prepare_manufacturer):
#     manufacturer = prepare_manufacturer
#     with patch("src.utils.image_manager.handle_image_upload", return_value="/upload/new_logo.png"):
#         response = client.patch(
#             f"/manufacturer/{manufacturer.manufacturer_id}",
#             files={"logo": ("new_logo.png", b"content", "image/png")},
#         )
#     assert response.status_code == 200
#     assert response.json()["status"] is True
#     assert response.json()["data"]["logo"].startswith("/upload/")


def test_delete_manufacturer_success(client, prepare_manufacturer):
    manufacturer = prepare_manufacturer  # НЕ await!
    response = client.delete(f"/manufacturer/{manufacturer.manufacturer_id}")
    assert response.status_code == 200
    assert response.json()["status"] is True


# --- UNIT TESTS (async) ---

@pytest.mark.asyncio
async def test_create_manufacturer_service(db_session: AsyncSession):
    data = ManufacturerCreateForm(
        name="ServiceCreate",
        seo_keyword="seo",
        meta_title="title",
        meta_description="desc",
        meta_keyword="keyword",
    )
    created = await ManufacturerService.create_manufacturer(db_session, data, logo_url="/assets/test.webp")
    assert created.name == "ServiceCreate"
    assert created.logo == "/assets/test.webp"


@pytest.mark.asyncio
async def test_get_manufacturer_by_id_service(db_session: AsyncSession):
    manufacturer = Manufacturer(name="ServiceGet")
    db_session.add(manufacturer)
    await db_session.commit()
    await db_session.refresh(manufacturer)

    found = await ManufacturerService.get_manufacturer_by_id(db_session, manufacturer.manufacturer_id)
    assert found.name == "ServiceGet"


@pytest.mark.asyncio
async def test_get_manufacturer_by_id_service_not_found(db_session: AsyncSession):
    with pytest.raises(ValueError):
        await ManufacturerService.get_manufacturer_by_id(db_session, 9999)


@pytest.mark.asyncio
async def test_update_manufacturer_service(db_session: AsyncSession):
    manufacturer = Manufacturer(name="ToUpdate")
    db_session.add(manufacturer)
    await db_session.commit()
    await db_session.refresh(manufacturer)

    updated = await ManufacturerService.update_manufacturer(
        db_session,
        manufacturer_id=manufacturer.manufacturer_id,
        update_data={"name": "Updated!"},
        logo_url="/upload/updated.webp"
    )
    assert updated.name == "Updated!"
    assert updated.logo == "/upload/updated.webp"


@pytest.mark.asyncio
async def test_delete_manufacturer_service(db_session: AsyncSession):
    manufacturer = Manufacturer(name="ToDelete")
    db_session.add(manufacturer)
    await db_session.commit()
    await db_session.refresh(manufacturer)

    await ManufacturerService.delete_manufacturer(db_session, manufacturer.manufacturer_id)

    stmt = select(Manufacturer).where(Manufacturer.manufacturer_id == manufacturer.manufacturer_id)
    result = await db_session.execute(stmt)
    assert result.scalar_one_or_none() is None
