from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession

import virtool.http.routes
import virtool.validators
import virtool.db.utils
from virtool.api.response import bad_request, empty_request, json_response, no_content, not_found
from virtool.labels.models import Label

routes = virtool.http.routes.Routes()


@routes.get("/api/labels")
async def find(req):
    """
    Get a list of all label documents in the database.

    """
    document = list()
    async with AsyncSession(req.app["postgres"]) as session:
        result = await session.execute(select(Label))
        labels = result.scalars().all()
        for label in labels:
            d = {
                "id": label.id,
                "name": label.name,
                "color": label.color,
                "description": label.description
            }
            document.append(d)

    return json_response(document)


@routes.get("/api/labels/{label_id}")
async def get(req):
    """
    Get a complete label document.

    """
    async with AsyncSession(req.app["postgres"]) as session:
        result = await session.execute(select(Label).filter_by(id=int(req.match_info["label_id"])))
        label = result.scalar()

        if label is None:
            return not_found()

        document = {
            "id": label.id,
            "name": label.name,
            "color": label.color,
            "description": label.description
        }

    return json_response(document)


@routes.post("/api/labels", schema={
    "name": {
        "type": "string",
        "coerce": virtool.validators.strip,
        "required": True,
        "empty": False
    },
    "color": {
        "type": "string",
        "coerce": virtool.validators.strip,
        "validator": virtool.validators.is_valid_hex_color,
        "default": "#A0AEC0"
    },
    "description": {
        "type": "string",
        "coerce": virtool.validators.strip,
        "default": ""
    }
})
async def create(req):
    """
    Add a new label to the labels database.

    """
    db = req.app["db"]
    data = req["data"]

    async with AsyncSession(req.app["postgres"]) as session:
        label = Label(name=data["name"], color=data["color"], description=data["description"])
        session.add(label)

        try:
            await session.flush()
            label_id = label.id
            await session.commit()
        except IntegrityError:
            return bad_request("Label name already exists")

    document = {
        "id": label_id,
        "name": data["name"],
        "color": data["color"],
        "description": data["description"]
    }

    headers = {
        "Location": f"/api/labels/{label_id}"
    }

    return json_response(document, status=201, headers=headers)


@routes.patch("/api/labels/{label_id}", schema={
    "name": {
        "type": "string",
        "coerce": virtool.validators.strip,
    },
    "color": {
        "type": "string",
        "coerce": virtool.validators.strip,
        "validator": virtool.validators.is_valid_hex_color,
    },
    "description": {
        "type": "string",
        "coerce": virtool.validators.strip,
    }
})
async def edit(req):
    """
    Edit an existing label.

    """
    data = req["data"]

    label_id = int(req.match_info["label_id"])

    if not data:
        return empty_request()

    async with AsyncSession(req.app["postgres"]) as session:
        result = await session.execute(select(Label).filter_by(id=label_id))
        label = result.scalar()

        if label is None:
            return not_found()

        label.name = data["name"]
        label.color = data["color"]
        label.description = data["description"]
        try:
            await session.commit()
        except IntegrityError:
            return bad_request("Label name already exists")

    document = {
        "id": label_id,
        "name": data["name"],
        "color": data["color"],
        "description": data["description"]
    }
    return json_response(document)


@routes.delete("/api/labels/{label_id}")
async def remove(req):
    """
    Remove a label.

    """
    label_id = int(req.match_info["label_id"])

    async with AsyncSession(req.app["postgres"]) as session:
        result = await session.execute(select(Label).filter_by(id=label_id))
        label = result.scalar()

        if label is None:
            return not_found()

        session.delete(label)
        await session.commit()

    return no_content()

@@ -191,16 +191,41 @@
 b.utils%0A
+import virtool.labels.db%0A
 from vir
@@ -1490,32 +1490,117 @@
 abel.description
+,%0A            %22count%22: await virtool.labels.db.count_samples(req.app%5B%22db%22%5D, label.id)
 %0A        %7D%0A%0A    
@@ -2728,32 +2728,32 @@
  data%5B%22color%22%5D,%0A
-
         %22descrip
@@ -2774,24 +2774,94 @@
 escription%22%5D
+,%0A        %22count%22: await virtool.labels.db.count_samples(db, label_id)
 %0A    %7D%0A%0A    

Return sample count in get and create endpoint