import io
from typing import Any

import gradio as gr
import httpx
from environs import Env
from gradio_image_annotation import image_annotator
from PIL import Image

env = Env()
env.read_env()

with env.prefixed("ERASER_"):
    API_URL: str = str(env.str("API_URL", "https://spaces.finegrain.ai/eraser"))
    API_KEY: str | None = env.str("API_KEY", None)
    CA_BUNDLE: str | None = env.str("CA_BUNDLE", None)

auth = None if API_KEY is None else httpx.BasicAuth("hf", API_KEY)


def process_bbox(prompts: dict[str, Any], request: gr.Request | None) -> Image.Image:
    assert isinstance(img := prompts["image"], Image.Image)
    assert isinstance(boxes := prompts["boxes"], list)
    assert len(boxes) == 1
    assert isinstance(box := boxes[0], dict)
    data = {"bbox": ",".join([str(box[k]) for k in ["xmin", "ymin", "xmax", "ymax"]])}
    headers = {}
    if request:  # avoid DOS - can be None despite type hint!
        client_ip = request.headers.get("x-forwarded-for") or request.client.host
        headers = {"X-HF-Client-IP": client_ip}
    with io.BytesIO() as f:
        img.save(f, format="JPEG")
        r = httpx.post(
            API_URL,
            data=data,
            files={"file": f},
            verify=CA_BUNDLE or True,
            timeout=30.0,
            auth=auth,
            headers=headers,
        )
    r.raise_for_status()
    return Image.open(io.BytesIO(r.content))


def on_change_bbox(prompts: dict[str, Any]):
    return gr.update(interactive=len(prompts["boxes"]) > 0)


def process_prompt(
    img: Image.Image,
    prompt: str,
    request: gr.Request | None,
) -> Image.Image:
    data = {"prompt": prompt}
    headers = {}
    if request:  # avoid DOS - can be None despite type hint!
        client_ip = request.headers.get("x-forwarded-for") or request.client.host
        headers = {"X-HF-Client-IP": client_ip}
    with io.BytesIO() as f:
        img.save(f, format="JPEG")
        r = httpx.post(
            API_URL,
            data=data,
            files={"file": f},
            verify=CA_BUNDLE or True,
            timeout=30.0,
            auth=auth,
            headers=headers,
        )
    r.raise_for_status()
    return Image.open(io.BytesIO(r.content))


def on_change_prompt(img: Image.Image | None, prompt: str | None):
    return gr.update(interactive=bool(img and prompt))


TITLE = """
<center>

  <h1 style="font-size: 1.5rem; margin-bottom: 0.5rem;">
    Object Eraser Powered By Refiners
  </h1>

  <div style="
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: 1.25rem;
    flex-wrap: wrap;
  ">
    <a href="https://github.com/finegrain-ai/refiners" target="_blank">[Refiners]</a>
    <a href="https://finegrain.ai/" target="_blank">[Finegrain]</a>
    <a
        href="https://huggingface.co/spaces/finegrain/finegrain-image-enhancer"
        target="_blank"
    >[Finegrain Image Enhancer]</a>
  </div>

  <p>
    Erase any object from your image just by naming it — no manual work required!
    Not only will the object disappear, but so will its effects on the scene, like shadows or reflections.
  </p>

  <p>
    This space is powered by Refiners, our open source micro-framework for simple foundation model adaptation.
    If you enjoyed it, please consider starring Refiners on GitHub!
  </p>

  <a href="https://github.com/finegrain-ai/refiners" target="_blank">
    <img src="https://img.shields.io/github/stars/finegrain-ai/refiners?style=social" />
  </a>

</center>
"""

with gr.Blocks() as demo:
    gr.HTML(TITLE)
    with gr.Tab("By prompt", id="tab_prompt"):
        with gr.Row():
            with gr.Column():
                iimg = gr.Image(type="pil", label="Input")
                prompt = gr.Textbox(label="What should we erase?")
            with gr.Column():
                oimg = gr.Image(show_label=False, label="Output")
        with gr.Row():
            btn = gr.Button("Erase Object", interactive=False)
        for inp in [iimg, prompt]:
            inp.change(
                fn=on_change_prompt,
                inputs=[iimg, prompt],
                outputs=[btn],
            )
        btn.click(
            fn=process_prompt,
            inputs=[iimg, prompt],
            outputs=[oimg],
            api_name=False,
        )

        examples = [
            [
                "examples/white-towels-rattan-basket-white-table-with-bright-room-background.jpg",
                "soap",
            ],
            [
                "examples/interior-decor-with-mirror-potted-plant.jpg",
                "potted plant",
            ],
            [
                "examples/detail-ball-basketball-court-sunset.jpg",
                "basketball",
            ],
            [
                "examples/still-life-device-table_23-2150994394.jpg",
                "glass of water",
            ],
            [
                "examples/knife-fork-green-checkered-napkin_140725-63576.jpg",
                "knife and fork",
            ],
            [
                "examples/city-night-with-architecture-vibrant-lights_23-2149836930.jpg",
                "frontmost black car on right lane",
            ],
            [
                "examples/close-up-coffee-latte-wooden-table_23-2147893063.jpg",
                "coffee cup on plate",
            ],
            [
                "examples/empty-chair-with-vase-plant_74190-2078.jpg",
                "chair",
            ],
        ]

        ex = gr.Examples(
            examples=examples,
            inputs=[iimg, prompt],
            outputs=[oimg],
            fn=process_prompt,
            cache_examples=True,
        )
    with gr.Tab("By bounding box", id="tab_bb"):
        with gr.Row():
            with gr.Column():
                annotator = image_annotator(
                    image_type="pil",
                    disable_edit_boxes=True,
                    show_download_button=False,
                    show_share_button=False,
                    single_box=True,
                    label="Input",
                )
            with gr.Column():
                oimg = gr.Image(show_label=False, label="Output")
        with gr.Row():
            btn = gr.Button("Erase Object", interactive=False)
        annotator.change(
            fn=on_change_bbox,
            inputs=[annotator],
            outputs=[btn],
        )
        btn.click(
            fn=process_bbox,
            inputs=[annotator],
            outputs=[oimg],
            api_name=False,
        )

        examples = [
            {
                "image": "examples/white-towels-rattan-basket-white-table-with-bright-room-background.jpg",
                "boxes": [{"xmin": 836, "ymin": 475, "xmax": 1125, "ymax": 1013}],
            },
            {
                "image": "examples/interior-decor-with-mirror-potted-plant.jpg",
                "boxes": [{"xmin": 47, "ymin": 907, "xmax": 397, "ymax": 1633}],
            },
            {
                "image": "examples/detail-ball-basketball-court-sunset.jpg",
                "boxes": [{"xmin": 673, "ymin": 954, "xmax": 911, "ymax": 1186}],
            },
            {
                "image": "examples/still-life-device-table_23-2150994394.jpg",
                "boxes": [{"xmin": 429, "ymin": 586, "xmax": 571, "ymax": 834}],
            },
            {
                "image": "examples/knife-fork-green-checkered-napkin_140725-63576.jpg",
                "boxes": [{"xmin": 972, "ymin": 226, "xmax": 1092, "ymax": 1023}],
            },
            {
                "image": "examples/city-night-with-architecture-vibrant-lights_23-2149836930.jpg",
                "boxes": [{"xmin": 215, "ymin": 637, "xmax": 411, "ymax": 855}],
            },
            {
                "image": "examples/close-up-coffee-latte-wooden-table_23-2147893063.jpg",
                "boxes": [{"xmin": 255, "ymin": 456, "xmax": 1080, "ymax": 1064}],
            },
            {
                "image": "examples/empty-chair-with-vase-plant_74190-2078.jpg",
                "boxes": [{"xmin": 35, "ymin": 320, "xmax": 383, "ymax": 983}],
            },
        ]

        ex = gr.Examples(
            examples=examples,
            inputs=[annotator],
            outputs=[oimg],
            fn=process_bbox,
            cache_examples=True,
        )

demo.queue(max_size=30, api_open=False)
demo.launch(show_api=False)