"""Template variable engine.

Supported variables (case-sensitive, %Delimited%):
  Recipient:   %FirstName% %LastName% %CompanyName% %CompanyPhone% %CustomerEmail%
               %Email% plus custom CSV columns (auto-detected)
  Generated:   %RandomInvoiceNumber% %RandomTotal% %RandomShippingCharge% %RandomName%
  Configured:  %InvoiceDate% %DueDate% %CompanyPhone%
  System:      %UnsubscribeLink%

Unknown variables produce validation warnings (surfaced before campaign start).
"""
import re
import secrets
import string
from dataclasses import dataclass, field
from datetime import date, datetime
from typing import Optional
VARIABLE_PATTERN = re.compile(r"%([A-Za-z0-9_]+)%")

KNOWN_VARIABLES = {
    "FirstName", "LastName", "CompanyName", "CompanyPhone", "CustomerEmail", "Email",
    "RandomInvoiceNumber", "RandomTotal", "RandomShippingCharge", "RandomName",
    "InvoiceDate", "DueDate", "UnsubscribeLink",
}


@dataclass
class GeneratedValues:
    invoice_number: str = ""
    amount: Optional[float] = None
    shipping: Optional[float] = None
    random_name: str = ""
    invoice_date: str = ""
    due_date: str = ""
    phone: str = ""


@dataclass
class SubstitutionResult:
    text: str
    unknown: list = field(default_factory=list)
    missing: list = field(default_factory=list)   # known but no data available
    used: list = field(default_factory=list)


class VariableEngine:
    def __init__(self, config: Optional[dict] = None, rng=None):
        self.config = config or {}
        self.rng = rng or secrets.SystemRandom()

    # ------------------------------------------------------------------ helpers
    def _cfg(self, key: str, default):
        return self.config.get(key, default)

    def generate_invoice_number(self, used: set = None) -> str:
        """Generate a unique invoice/reference number using configurable format tokens.

        Tokens: {prefix} {R#} uppercase {r#} lowercase {d#} digits {seq4} {date} {hash}
        Cryptographically strong randomness via SystemRandom.
        """
        used = used if used is not None else set()
        fmt = self._cfg("invoice_format", "{prefix}{R4}-{R5}-{R6}-{seq4}")
        prefix = self._cfg("invoice_prefix", "INV-")
        for _ in range(50):
            val = self._render_format(fmt, prefix)
            if val not in used:
                return val
        raise ValueError("Could not generate unique invoice number after 50 attempts")

    def _render_format(self, fmt: str, prefix: str) -> str:
        from string import ascii_letters, digits
        upper = string.ascii_uppercase + string.digits
        lower = string.ascii_lowercase + string.digits
        digits_only = string.digits

        def rnd(chars, n):
            return "".join(self.rng.choice(chars) for _ in range(n))

        def repl(match):
            token = match.group(1)
