"""Literal substitutions only; no execution, attribute access or formatting."""
from string import Formatter
from django.core.exceptions import ValidationError

PLACEHOLDERS = frozenset({"customer_name", "appointment_date", "appointment_time", "service_case_reference",
    "device_name", "quotation_reference", "invoice_reference", "amount_due", "payment_amount", "receipt_reference"})


def fields(text):
    try:
        parsed = list(Formatter().parse(text))
    except ValueError as error:
        raise ValidationError("Malformed placeholder braces.") from error
    for _, name, spec, conversion in parsed:
        if name is not None and (name not in PLACEHOLDERS or spec or conversion):
            raise ValidationError("Unsupported placeholder. Use simple supported names in braces.")
    return {name for _, name, _, _ in parsed if name is not None}


def validate_template(subject, body, channel):
    fields(subject)
    fields(body)
    if not body.strip() or (channel == "EMAIL" and not subject.strip()):
        raise ValidationError("A body and an email subject are required.")
    if channel == "SMS" and subject:
        raise ValidationError("SMS templates have no subject.")
    if "\n" in subject or "\r" in subject:
        raise ValidationError("Subject must be a single line.")


def render(template, context):
    needed = fields(template.subject) | fields(template.body)
    if not needed <= context.keys():
        raise ValidationError("Template needs context not available for this event.")
    values = {key: str(context[key]) for key in needed}
    subject, body = template.subject.format_map(values), template.body.format_map(values)
    validate_template(template.subject, template.body, template.channel)
    if len(subject) > 200 or len(body) > 4000 or "\r" in subject or "\n" in subject:
        raise ValidationError("Rendered message exceeds limits or has an invalid subject.")
    return subject, body
