import base64
from pathlib import Path

import pytest

import dotpromptz
from dotpromptz import build_prompt, compile_prompt, plan_prompt, render_compiled_prompt
from dotpromptz.typing import MediaPart
from dotpromptz.yamlio import load_yaml


def _write_prompt(path: Path, content: str) -> Path:
    path.write_text(content, encoding='utf-8')
    return path


def test_public_api_exposes_new_services_only() -> None:
    assert hasattr(dotpromptz, 'compile_prompt')
    assert hasattr(dotpromptz, 'plan_prompt')
    assert hasattr(dotpromptz, 'build_prompt')

    assert not hasattr(dotpromptz, 'PromptEngine')
    assert not hasattr(dotpromptz, 'run')
    assert not hasattr(dotpromptz, 'resolve_adapter')


def test_compile_and_plan_prompt_pipeline(tmp_path: Path) -> None:
    prompt_file = _write_prompt(
        tmp_path / 'sample.prompt',
        """---
version: 1
adapter: openai
output:
  format: json
  file_name: "result_{{name}}"
input:
  data:
    name: Alice
---
:::user
Hello {{name}}
""",
    )

    compiled = compile_prompt(prompt_file)
    rendered = render_compiled_prompt(compiled, compiled.input_list[0])
    plan = plan_prompt(compiled, batch_time='20260101_000000')

    assert compiled.prompt_path == prompt_file
    assert rendered.messages[0].role == 'user'
    assert len(plan.items) == 1
    assert plan.items[0].output_stem == 'result_Alice'
    assert plan.items[0].output_path.name == 'result_Alice.json'


def test_build_prompt_emits_yaml_output(tmp_path: Path) -> None:
    prompt_file = _write_prompt(
        tmp_path / 'build.prompt',
        """---
version: 1
adapter: openai
output:
  format: json
  file_name: "demo"
---
:::user
hello
""",
    )
    report = build_prompt(prompt_file, log_dir=tmp_path)
    assert report.output_file.exists()
    assert report.rendered.messages[0].role == 'user'
    data = load_yaml(report.output_file.read_text(encoding='utf-8'))
    assert data['messages'][0]['role'] == 'user'


def test_plan_prompt_uses_png_extension_for_image_output(tmp_path: Path) -> None:
    prompt_file = _write_prompt(
        tmp_path / 'image_out.prompt',
        """---
version: 1
adapter: openai
output:
  format: image
  file_name: "image_{{name}}"
input:
  data:
    name: Alice
---
:::user
draw a cat
""",
    )
    compiled = compile_prompt(prompt_file)
    plan = plan_prompt(compiled, batch_time='20260101_000000')
    assert plan.items[0].output_path.name == 'image_Alice.png'


def test_render_compiled_prompt_resolves_relative_image_path(tmp_path: Path) -> None:
    prompt_file = _write_prompt(
        tmp_path / 'image.prompt',
        """---
version: 1
adapter: openai
output:
  format: json
  file_name: "demo"
---
:::user
describe this image
:::image
assets/red.png
""",
    )
    image_path = tmp_path / 'assets' / 'red.png'
    image_path.parent.mkdir(parents=True, exist_ok=True)
    image_path.write_bytes(
        base64.b64decode(
            'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4////fwAJ+wP9KobjigAAAABJRU5ErkJggg=='
        )
    )

    compiled = compile_prompt(prompt_file)
    rendered = render_compiled_prompt(compiled, compiled.input_list[0])

    media_part = rendered.messages[0].content[1]
    assert isinstance(media_part, MediaPart)
    assert media_part.media.url == str(image_path.resolve())


def test_render_compiled_prompt_rejects_data_uri_image(tmp_path: Path) -> None:
    prompt_file = _write_prompt(
        tmp_path / 'data_uri.prompt',
        """---
version: 1
adapter: openai
output:
  format: json
  file_name: "demo"
---
:::user
describe this image
:::image
data:image/png;base64,abc123
""",
    )
    compiled = compile_prompt(prompt_file)
    with pytest.raises(ValueError, match='data: image URLs are not supported'):
        render_compiled_prompt(compiled, compiled.input_list[0])
