"""Replacement preview rendering for batch-source commands."""

from __future__ import annotations

from collections.abc import Callable

from ..selection import replacement_selection
from ...batch.replacement import build_replacement_batch_view_from_lines
from ...batch.selection import acquire_batch_ownership_for_display_ids_from_lines
from ...batch.state.metadata_types import BatchFileMetadataDict
from ...batch.submodule_pointer import is_batch_submodule_pointer
from ...core.replacement import ReplacementPayload, coerce_replacement_payload
from ...core.models import RenderedBatchDisplay
from ...utils.context_lines import get_context_lines
from ...data.file_review.batch_selection import (
    translate_batch_file_gutter_ids_to_selection_ids,
)
from ...data.file_review.records import FileReviewAction
from ...utils.repository_buffers import read_git_object_buffer_or_none
from ...exceptions import exit_with_error
from ...i18n import _
from ...output.candidate_preview_diff import render_candidate_buffer_diff


SelectionTranslator = Callable[
    [str, str, set[int] | None, FileReviewAction | str],
    tuple[set[int] | None, RenderedBatchDisplay | None],
]


def print_batch_source_replacement_preview(
    *,
    batch_name: str,
    files: dict[str, BatchFileMetadataDict],
    file_path: str,
    selected_ids: set[int],
    replacement_text: str | ReplacementPayload,
    translate_selection_ids: SelectionTranslator = (
        translate_batch_file_gutter_ids_to_selection_ids
    ),
) -> None:
    """Print a diff preview for replacing selected batch-source lines."""
    file_meta = files[file_path]
    if file_meta.get("file_type") == "binary":
        exit_with_error(_("Cannot preview replacement text for binary files."))
    if is_batch_submodule_pointer(file_meta):
        exit_with_error(_("Cannot preview replacement text for submodule pointers."))

    replacement_selection.require_contiguous_display_selection(selected_ids)
    batch_source_commit = file_meta["batch_source_commit"]
    batch_source_buffer = read_git_object_buffer_or_none(
        f"{batch_source_commit}:{file_path}"
    )
    if batch_source_buffer is None:
        exit_with_error(
            _("Batch source content is missing for {file}.").format(file=file_path)
        )

    with batch_source_buffer as batch_source_lines:
        selection_ids, _rendered = translate_selection_ids(
            batch_name,
            file_path,
            selected_ids,
            FileReviewAction.INCLUDE_FROM_BATCH,
        )
        if selection_ids is None:
            raise ValueError("selection translation omitted selected lines")
        with acquire_batch_ownership_for_display_ids_from_lines(
            file_meta,
            batch_source_lines,
            selection_ids,
        ) as ownership:
            try:
                replacement_view = build_replacement_batch_view_from_lines(
                    batch_source_lines,
                    ownership,
                    coerce_replacement_payload(replacement_text),
                )
            except ValueError as e:
                exit_with_error(str(e))
            with replacement_view:
                diff_text = render_candidate_buffer_diff(
                    file_path,
                    batch_source_buffer,
                    replacement_view.source_buffer,
                    label_before="batch",
                    label_after="replacement-preview",
                    context_lines=get_context_lines(),
                )
                if diff_text:
                    print(
                        diff_text,
                        end="" if diff_text.endswith("\n") else "\n",
                    )
