"""Script generation via Amazon Bedrock Converse API.

Transforms source material into a structured conversational script using
mode-specific system prompts. Handles long content by chunking with overlap
and performing a summarization pre-pass.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from alice_cli.bedrock_utils import build_converse_kwargs, parse_converse_response
from alice_cli.errors import AliceCLIError
from alice_cli.models import TokenUsage
from alice_cli.teaparty.models import GenerationMode, Script, ScriptParseError, parse_script
from alice_cli.teaparty.prompts import get_system_prompt

# Heuristic: 1 token ≈ 4 characters
_CHARS_PER_TOKEN = 4

# Overlap between consecutive chunks (in tokens)
_OVERLAP_TOKENS = 500


@dataclass
class GenerationResult:
    """Result of script generation."""

    script: Script
    tokens: TokenUsage


def _estimate_tokens(text: str) -> int:
    """Estimate token count using chars/4 heuristic."""
    return len(text) // _CHARS_PER_TOKEN


def chunk_text(text: str, max_input_tokens: int) -> list[str]:
    """Split text into chunks with 500-token overlap.

    Each chunk is at most ``max_input_tokens`` tokens (estimated via chars/4).
    Consecutive chunks overlap by approximately 500 tokens (2000 chars).

    Args:
        text: The source text to chunk.
        max_input_tokens: Maximum tokens per chunk.

    Returns:
        List of text chunks.
    """
    max_chars = max_input_tokens * _CHARS_PER_TOKEN
    overlap_chars = _OVERLAP_TOKENS * _CHARS_PER_TOKEN

    if len(text) <= max_chars:
        return [text]

    # Guard: overlap must be smaller than chunk size to make forward progress
    effective_overlap = min(overlap_chars, max_chars - 1) if max_chars > 1 else 0

    chunks: list[str] = []
    start = 0
    while start < len(text):
        end = start + max_chars
        if end >= len(text):
            chunks.append(text[start:])
            break
        chunks.append(text[start:end])
        start = end - effective_overlap

    return chunks


def _summarize_chunk(
    chunk: str,
    client: Any,
    model_id: str,
) -> tuple[str, TokenUsage]:
    """Summarize a single text chunk via Bedrock.

    Args:
        chunk: Text to summarize.
        client: BedrockClient instance.
        model_id: Bedrock model ID.

    Returns:
        Tuple of (summary_text, token_usage).
    """
    messages = [
        {
            "role": "user",
            "content": [{"text": f"Summarize the following text concisely:\n\n{chunk}"}],
        }
    ]
    system_prompt = (
        "You are a helpful summarizer. Produce a concise summary preserving key facts and details."
    )
    kwargs = build_converse_kwargs(model_id, messages, system_prompt)
    response = client.converse(**kwargs)
    return parse_converse_response(response)


def generate_script(
    source_text: str,
    client: Any,
    model_id: str,
    mode: GenerationMode = GenerationMode.CONVERSATION,
    lang: str = "en-US",
    max_input_tokens: int = 180_000,
) -> GenerationResult:
    """Generate a conversational script from source material.

    Uses the mode-specific system prompt from prompts.py. If source_text
    exceeds max_input_tokens, performs a summarization pre-pass: splits
    into chunks with 500-token overlap, summarizes each chunk, then
    concatenates summaries as input for the final generation call.

    Args:
        source_text: The source material text.
        client: BedrockClient instance with a ``.converse()`` method.
        model_id: Bedrock model ID or inference profile ARN.
        mode: Script generation style.
        lang: Target language code (e.g. "en-US", "es-ES").
        max_input_tokens: Token threshold for summarization pre-pass.

    Returns:
        GenerationResult with the parsed Script and aggregated TokenUsage.

    Raises:
        AliceCLIError: If the Bedrock call fails.
        ScriptParseError: If the model response is not valid script JSON.
    """
    total_input = 0
    total_output = 0

    estimated_tokens = _estimate_tokens(source_text)
    effective_text = source_text

    # Long content: summarization pre-pass
    if estimated_tokens > max_input_tokens:
        chunks = chunk_text(source_text, max_input_tokens)
        summaries: list[str] = []
        for chunk in chunks:
            summary, usage = _summarize_chunk(chunk, client, model_id)
            summaries.append(summary)
            total_input += usage.input
            total_output += usage.output
        effective_text = "\n\n".join(summaries)

    # Build the script generation request
    system_prompt = get_system_prompt(mode, lang)
    messages = [
        {
            "role": "user",
            "content": [{"text": effective_text}],
        }
    ]
    kwargs = build_converse_kwargs(model_id, messages, system_prompt)

    try:
        response = client.converse(**kwargs)
    except Exception as exc:
        raise AliceCLIError(f"Bedrock script generation failed: {exc}") from exc

    response_text, usage = parse_converse_response(response)
    total_input += usage.input
    total_output += usage.output

    try:
        script = parse_script(response_text)
    except ScriptParseError:
        raise
    except Exception as exc:
        raise ScriptParseError(f"Failed to parse script from model response: {exc}") from exc

    return GenerationResult(
        script=script,
        tokens=TokenUsage(
            input=total_input,
            output=total_output,
            total=total_input + total_output,
        ),
    )
