from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable

try:
    from . import formatter_input as input_policy
    from . import formatter_route_trace as trace_policy
    from . import formatter_target as target_policy
except ImportError:  # pragma: no cover - plain-script smoke tests
    import formatter_input as input_policy
    import formatter_route_trace as trace_policy
    import formatter_target as target_policy


@dataclass(frozen=True)
class CaptionFormatRequest:
    source_text: str
    metadata_json: str = ""
    input_hint: str = "auto"
    target: str = "auto"
    trigger: str = ""
    include_trigger: bool = True
    detail_level: str = "balanced"
    style_policy: str = "drop_style_tail"
    caption_profile: str = "manual_controls"


@dataclass(frozen=True)
class CaptionFormatRoute:
    caption: str
    method: str
    branch: str
    input_hint: str
    target: str
    detail_level: str
    style_policy: str
    include_trigger: bool
    keep_style: bool
    route_trace_json: str = ""

    def as_tuple(self) -> tuple[str, str]:
        return self.caption, self.method

    def as_trace_tuple(self) -> tuple[str, str, str]:
        return self.caption, self.method, self.route_trace_json


@dataclass(frozen=True)
class CaptionFormatDependencies:
    apply_caption_profile: Callable[[str, str, str, bool], tuple[str, str, bool]]
    keep_style_terms: Callable[[str], bool]
    row_from_inputs: Callable[[str, str, str], tuple[dict[str, Any] | None, str]]
    metadata_to_prose: Callable[..., tuple[str, str]]
    text_to_prose: Callable[[str, str, bool], tuple[str, str]]
    with_trigger: Callable[[str, str, bool], str]
    sanitize_prose_text: Callable[..., str]


def naturalize_caption_result(
    request: CaptionFormatRequest,
    deps: CaptionFormatDependencies,
) -> CaptionFormatRoute:
    input_hint = input_policy.normalize_input_hint(request.input_hint, text_hint=input_policy.INPUT_HINT_CAPTION_OR_PROMPT)
    target = target_policy.normalize_target(request.target)
    detail_level, style_policy, include_trigger = deps.apply_caption_profile(
        request.caption_profile,
        request.detail_level,
        request.style_policy,
        request.include_trigger,
    )
    keep_style = deps.keep_style_terms(style_policy)
    row, row_method = deps.row_from_inputs(request.source_text, request.metadata_json, input_hint)
    if row is not None:
        prose, method = deps.metadata_to_prose(row, detail_level, keep_style, target)
        caption = deps.sanitize_prose_text(
            deps.with_trigger(prose, request.trigger, include_trigger),
            triggers=(request.trigger,),
        )
        full_method = f"{row_method}:{method}"
        route_trace = trace_policy.route_trace_json(
            formatter="caption",
            branch="metadata",
            method=full_method,
            input_hint=input_hint,
            target=target,
            detail_level=detail_level,
            style_policy=style_policy,
            include_trigger=include_trigger,
            keep_style=keep_style,
            **trace_policy.metadata_trace_fields(row, target=target),
        )
        return CaptionFormatRoute(
            caption=caption,
            method=full_method,
            branch="metadata",
            input_hint=input_hint,
            target=target,
            detail_level=detail_level,
            style_policy=style_policy,
            include_trigger=include_trigger,
            keep_style=keep_style,
            route_trace_json=route_trace,
        )

    prose, method = deps.text_to_prose(request.source_text, detail_level, keep_style)
    caption = deps.sanitize_prose_text(
        deps.with_trigger(prose, request.trigger, include_trigger),
        triggers=(request.trigger,),
    )
    route_trace = trace_policy.route_trace_json(
        formatter="caption",
        branch="text",
        method=method,
        input_hint=input_hint,
        target=target,
        detail_level=detail_level,
        style_policy=style_policy,
        include_trigger=include_trigger,
        keep_style=keep_style,
    )
    return CaptionFormatRoute(
        caption=caption,
        method=method,
        branch="text",
        input_hint=input_hint,
        target=target,
        detail_level=detail_level,
        style_policy=style_policy,
        include_trigger=include_trigger,
        keep_style=keep_style,
        route_trace_json=route_trace,
    )


def naturalize_caption(
    request: CaptionFormatRequest,
    deps: CaptionFormatDependencies,
) -> tuple[str, str]:
    return naturalize_caption_result(request, deps).as_tuple()
