"""Prompt templates for LLM-based structured extraction."""

from __future__ import annotations

import json

from extraction.models import ExtractionSchema, FieldType

_FIELD_TYPE_HINTS: dict[FieldType, str] = {
    FieldType.TEXT: "plain text string",
    FieldType.NUMBER: "numeric value (integer or decimal)",
    FieldType.DATE: "date in ISO 8601 format (YYYY-MM-DD)",
    FieldType.BOOLEAN: "true or false",
    FieldType.CURRENCY: "monetary amount with currency code (e.g. 'USD 1500.00')",
    FieldType.ENTITY: "named entity (person, organization, or location)",
    FieldType.LIST: "list of strings",
}


def build_extraction_prompt(schema: ExtractionSchema) -> str:
    """Build a system prompt from an extraction schema.

    Generates a deterministic prompt that instructs the LLM to extract
    structured data matching the schema.  Each field is listed with its
    type, description, and examples.

    Args:
        schema: The extraction schema describing which fields to extract.

    Returns:
        A system-prompt string ready for use in an LLM call.
    """
    lines: list[str] = [
        "You are a structured data extraction engine.",
        "Extract the following fields from the provided text.",
        "Return ONLY a valid JSON object matching the schema below -- no explanation, "
        "no markdown fences, no extra keys.",
        "",
    ]

    if schema.description:
        lines.append(f"Schema: {schema.name} -- {schema.description}")
    else:
        lines.append(f"Schema: {schema.name}")
    lines.append("")

    lines.append("Fields to extract:")
    for field in schema.fields:
        type_hint = _FIELD_TYPE_HINTS.get(field.field_type, "string")
        required_label = "REQUIRED" if field.required else "optional"
        entry = f"  - {field.name} ({type_hint}, {required_label})"
        if field.description:
            entry += f": {field.description}"
        lines.append(entry)
        if field.examples:
            examples_str = ", ".join(f'"{ex}"' for ex in field.examples)
            lines.append(f"    Examples: {examples_str}")

    lines.append("")
    lines.append("Expected JSON schema:")
    lines.append(json.dumps(schema.to_json_schema(), indent=2))
    lines.append("")
    lines.append("Rules:")
    lines.append("  - Return null for any field you cannot confidently extract.")
    lines.append("  - Do NOT invent or hallucinate values.")
    lines.append("  - For dates, use ISO 8601 format (YYYY-MM-DD).")
    lines.append("  - For currency, include the currency code (e.g. 'USD 1500.00').")
    lines.append("  - For lists, return an array of strings.")

    return "\n".join(lines)


def build_user_prompt(text: str, *, max_chars: int | None = None) -> str:
    """Wrap user text for extraction.

    Args:
        text: The raw text to extract data from.
        max_chars: Optional character limit.  Text exceeding this length
            is truncated with a notice appended.

    Returns:
        The formatted user prompt string.
    """
    if max_chars is not None and len(text) > max_chars:
        text = text[:max_chars] + "\n\n[TEXT TRUNCATED]"

    return f"Extract structured data from the following text:\n\n{text}"


def build_classification_prompt(categories: list[str]) -> str:
    """Build a system prompt for text classification.

    Args:
        categories: The list of valid category labels.

    Returns:
        A system-prompt string for classification tasks.
    """
    categories_str = ", ".join(f'"{c}"' for c in categories)
    return (
        "You are a text classification engine.\n"
        f"Classify the provided text into exactly ONE of these categories: {categories_str}\n\n"
        "Return ONLY a JSON object with two keys:\n"
        '  - "category": the chosen category (must be one of the listed categories)\n'
        '  - "confidence": a number between 0 and 1 indicating your confidence\n\n'
        "No explanation, no markdown fences, no extra keys."
    )
