#!/usr/bin/env python3
"""
Qwen-Image OpenAI-compatible image generation client.

Usage:
    python openai_chat_client.py --prompt "A beautiful landscape" --output output.png
    python openai_chat_client.py --prompt "A sunset" --height 1024 --width 1024 --steps 50 --seed 42
"""

import argparse
import base64
from pathlib import Path

import requests


def generate_image(
    prompt: str,
    server_url: str = "http://localhost:8091",
    height: int | None = None,
    width: int | None = None,
    steps: int | None = None,
    true_cfg_scale: float | None = None,
    seed: int | None = None,
    negative_prompt: str | None = None,
    num_outputs_per_prompt: int = 1,
    lora_path: str | None = None,
    lora_name: str | None = None,
    lora_scale: float | None = None,
    lora_int_id: int | None = None,
    use_system_prompt: str | None = None,
    system_prompt: str | None = None,
) -> bytes | None:
    """Generate an image using the images generation API.

    Args:
        prompt: Text description of the image
        server_url: Server URL
        height: Image height in pixels
        width: Image width in pixels
        steps: Number of diffusion steps
        true_cfg_scale: Qwen-Image CFG scale
        seed: Random seed
        negative_prompt: Negative prompt
        num_outputs_per_prompt: Number of images to generate
        lora_path: Server-local LoRA adapter folder path (PEFT format)
        lora_name: LoRA name (optional, defaults to path stem)
        lora_scale: LoRA scale factor (default: 1.0)
        lora_int_id: LoRA integer ID (optional, derived from path if not provided)
        use_system_prompt: System prompt for generation.
        system_prompt: Custom system prompt.

    Returns:
        Image bytes or None if failed
    """
    payload: dict[str, object] = {
        "prompt": prompt,
        "response_format": "b64_json",
        "n": num_outputs_per_prompt,
    }

    if width is not None and height is not None:
        payload["size"] = f"{width}x{height}"
    elif width is not None:
        payload["size"] = f"{width}x{width}"
    elif height is not None:
        payload["size"] = f"{height}x{height}"

    if steps is not None:
        payload["num_inference_steps"] = steps
    if true_cfg_scale is not None:
        payload["true_cfg_scale"] = true_cfg_scale
    if negative_prompt:
        payload["negative_prompt"] = negative_prompt
    if seed is not None:
        payload["seed"] = seed
    if use_system_prompt is not None:
        payload["use_system_prompt"] = use_system_prompt
    if system_prompt is not None:
        payload["system_prompt"] = system_prompt
    # Add LoRA if provided
    if lora_path:
        lora_body: dict = {
            "local_path": lora_path,
            "name": lora_name or Path(lora_path).stem,
        }
        if lora_scale is not None:
            lora_body["scale"] = float(lora_scale)
        if lora_int_id is not None:
            lora_body["int_id"] = int(lora_int_id)
        payload["lora"] = lora_body

    try:
        response = requests.post(
            f"{server_url}/v1/images/generations",
            headers={"Content-Type": "application/json"},
            json=payload,
            timeout=300,
        )
        response.raise_for_status()
        data = response.json()

        items = data.get("data")
        if isinstance(items, list) and items:
            first = items[0].get("b64_json") if isinstance(items[0], dict) else None
            if isinstance(first, str):
                return base64.b64decode(first)

        print(f"Unexpected response format: {data}")
        return None

    except Exception as e:
        print(f"Error: {e}")
        return None


def main():
    parser = argparse.ArgumentParser(description="Qwen-Image chat client")
    parser.add_argument("--prompt", "-p", default="a cup of coffee on the table", help="Text prompt")
    parser.add_argument("--output", "-o", default="qwen_image_output.png", help="Output file")
    parser.add_argument("--server", "-s", default="http://localhost:8091", help="Server URL")
    parser.add_argument("--height", type=int, default=1024, help="Image height")
    parser.add_argument("--width", type=int, default=1024, help="Image width")
    parser.add_argument("--steps", type=int, default=50, help="Inference steps")
    parser.add_argument("--cfg-scale", type=float, default=4.0, help="True CFG scale")
    parser.add_argument("--seed", type=int, default=0, help="Random seed")
    parser.add_argument("--negative", help="Negative prompt")

    parser.add_argument("--lora-path", default=None, help="Server-local LoRA adapter folder (PEFT format)")
    parser.add_argument("--lora-name", default=None, help="LoRA name (optional)")
    parser.add_argument("--lora-scale", type=float, default=1.0, help="LoRA scale")
    parser.add_argument(
        "--lora-int-id",
        type=int,
        default=None,
        help="LoRA integer id (cache key). If omitted, the server derives a stable id from lora_path.",
    )
    parser.add_argument(
        "--use-system-prompt",
        type=str,
        default=None,
        help=(
            "System prompt for generation. Use predefined types: 'en_unified', 'en_vanilla', 'en_recaption', 'en_think_recaption', 'dynamic', or 'None'; Or provide custom text string directly. Recommended en_unified. "
        ),
    )
    parser.add_argument(
        "--system-prompt",
        type=str,
        default=None,
        help=("Custom system prompt. Used when --use-system-prompt is custom. "),
    )
    args = parser.parse_args()
    print(f"Generating image for: {args.prompt}")

    image_bytes = generate_image(
        prompt=args.prompt,
        server_url=args.server,
        height=args.height,
        width=args.width,
        steps=args.steps,
        true_cfg_scale=args.cfg_scale,
        seed=args.seed,
        negative_prompt=args.negative,
        lora_path=args.lora_path,
        lora_name=args.lora_name,
        lora_scale=args.lora_scale if args.lora_path else None,
        lora_int_id=args.lora_int_id if args.lora_path else None,
        use_system_prompt=args.use_system_prompt,
        system_prompt=args.system_prompt,
    )

    if image_bytes:
        output_path = Path(args.output)
        output_path.write_bytes(image_bytes)
        print(f"Image saved to: {output_path}")
        print(f"Size: {len(image_bytes) / 1024:.1f} KB")
    else:
        print("Failed to generate image")
        exit(1)


if __name__ == "__main__":
    main()
