import asyncio
import os
from pathlib import Path
from typing import Optional

import torch
from diffusers import AutoPipelineForText2Image
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field

MODEL_ID = os.environ.get('IMAGE_MODEL_ID', 'stabilityai/sd-turbo')
OUTPUT_DIR = Path(os.environ.get('IMAGE_OUTPUT_DIR', 'content/generated/global-words')).resolve()
DEFAULT_GUIDANCE = float(os.environ.get('IMAGE_GUIDANCE_SCALE', '0.0' if 'turbo' in MODEL_ID else '5.0'))

device = 'cuda' if torch.cuda.is_available() else 'cpu'
dtype = torch.float16 if device == 'cuda' else torch.float32

pipe = AutoPipelineForText2Image.from_pretrained(
    MODEL_ID,
    torch_dtype=dtype,
    trust_remote_code=True,
).to(device)
pipe.set_progress_bar_config(disable=True)

generation_lock = asyncio.Lock()


class GenerateRequest(BaseModel):
    prompt: str
    slug: str
    negative_prompt: Optional[str] = None
    width: int = Field(640, ge=256, le=1024)
    height: int = Field(512, ge=256, le=1024)
    steps: int = Field(4, ge=1, le=30)
    guidance: Optional[float] = Field(None, ge=0.0, le=20.0)
    seed: Optional[int] = None


class GenerateResponse(BaseModel):
    path: str
    mimeType: str = 'image/png'


def snap_resolution(value: int) -> int:
    clipped = max(256, min(1024, value))
    return (clipped // 8) * 8


app = FastAPI(title='Local Image Service', version='1.0.0')


@app.post('/generate', response_model=GenerateResponse)
async def generate_image(req: GenerateRequest):
    if not req.prompt.strip():
        raise HTTPException(status_code=400, detail='Prompt is required')

    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

    generator = None
    if req.seed is not None:
        gen_device = device if device == 'cuda' else 'cpu'
        generator = torch.Generator(gen_device).manual_seed(req.seed)

    kwargs = {
        'prompt': req.prompt,
        'negative_prompt': req.negative_prompt,
        'num_inference_steps': req.steps,
        'guidance_scale': req.guidance if req.guidance is not None else DEFAULT_GUIDANCE,
        'width': snap_resolution(req.width),
        'height': snap_resolution(req.height),
    }
    if generator is not None:
        kwargs['generator'] = generator

    async with generation_lock:
        image = pipe(**kwargs).images[0]

    file_path = OUTPUT_DIR / f'{req.slug}.png'
    image.save(file_path)
    return GenerateResponse(path=str(file_path.resolve()))
