"""Email template renderer — Jinja2-based with personalization support.

Provides:
- Template loading from disk (built-in defaults + user custom templates)
- Personalization: {{recipient.email}}, {{recipient.first_name}}, {{form.field_name}}
- Fallback chain: campaign template → email_settings template → default
- Plain-text extraction from HTML for email fallback
"""

import logging
import os
import re

from jinja2 import Environment, FileSystemLoader, select_autoescape

logger = logging.getLogger("agentforms.emails")

# Directories
_EMAILS_DIR = os.path.dirname(__file__)
_BUILTIN_TEMPLATES_DIR = os.path.join(_EMAILS_DIR, "templates")
_CUSTOM_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.dirname(_EMAILS_DIR)), "data", "custom_templates")

# Base URL for tracking
_BASE_URL = os.environ.get("APP_URL", "https://agentforms.io")


class EmailTemplateRenderer:
    """Jinja2-based email template renderer with personalization."""

    def __init__(self, base_url=None):
        self.base_url = base_url or _BASE_URL

        # Combined loader: custom templates take precedence over builtins
        searchpaths = [
            _CUSTOM_TEMPLATES_DIR,
            _BUILTIN_TEMPLATES_DIR,
        ]

        self.env = Environment(
            loader=FileSystemLoader(searchpaths),
            autoescape=select_autoescape(default=True),
            trim_blocks=True,
            lstrip_blocks=True,
        )

        # Register email-specific filters
        self.env.filters["truncate_html"] = self._truncate_html
        self.env.filters["plain_text"] = self._html_to_plain
        self.env.filters["default"] = self._default_filter

    @staticmethod
    def _truncate_html(html, length=200):
        """Truncate HTML content to approx N characters, stripping tags."""
        if not html:
            return ""
        plain = re.sub(r"<[^>]+>", "", html)
        if len(plain) <= length:
            return html
        return plain[:length] + "..."

    @staticmethod
    def _html_to_plain(html):
        """Strip HTML tags for plain-text fallback."""
        if not html:
            return ""
        return re.sub(r"<[^>]+>", "", html).strip()

    @staticmethod
    def _default_filter(value, default=""):
        """Safe default filter that handles None."""
        if value is None:
            return default
        return value if value else default

    def render(self, template_id, context=None):
        """Render a template with the given context.

        Args:
            template_id: Template identifier (e.g. 'default', 'survey', 'onboarding')
            context: Dict of template variables. Supports:
                - subject: Email subject line
                - form_name: Name of the form
                - body: Email body content (HTML)
                - cta_url: Call-to-action URL (form link)
                - cta_text: CTA button text
                - sender_name: Sender display name
                - recipient: Dict with recipient data (email, first_name, etc.)
                - field_preview: HTML preview of form fields
                - preview_text: Inbox preview text
                - unsubscribe_url: CAN-SPAM unsubscribe link
                - physical_address: CAN-SPAM physical address
                - base_url: Application base URL

        Returns:
            Rendered HTML string
        """
        context = context or {}
        context.setdefault("base_url", self.base_url)

        # Resolve template name
        name = template_id or "default"
        if not name.endswith(".html"):
            name = f"{name}.html"

        try:
            template = self.env.get_template(name)
            return template.render(**context)
        except Exception as e:
            logger.error("Template render failed for %s: %s", template_id, e)
            # Fallback to default template on error
            try:
                template = self.env.get_template("default.html")
                return template.render(**context)
            except Exception as fallback_err:
                logger.error("Fallback template also failed: %s", fallback_err)
                return "<html><body><p>Error rendering email.</p></body></html>"

    def render_subject(self, subject_template, context=None):
        """Render a subject line template with personalization.

        Supports {{recipient.email}}, {{recipient.first_name}}, {{form_name}}, etc.

        Args:
            subject_template: Subject line with Jinja2 variables
            context: Dict of template variables (same as render())

        Returns:
            Rendered subject line string
        """
        context = context or {}
        context.setdefault("base_url", self.base_url)

        try:
            tpl = self.env.from_string(subject_template)
            return tpl.render(**context)
        except Exception as e:
            logger.warning("Subject render failed: %s — returning raw", e)
            return subject_template

    def list_templates(self):
        """List all available templates (built-in + custom).

        Returns:
            List of dicts with 'name', 'type' (builtin/custom), 'path'
        """
        templates = []

        # Built-in templates
        if os.path.isdir(_BUILTIN_TEMPLATES_DIR):
            for fname in sorted(os.listdir(_BUILTIN_TEMPLATES_DIR)):
                if fname.endswith(".html"):
                    templates.append(
                        {
                            "name": fname.replace(".html", ""),
                            "type": "builtin",
                            "path": os.path.join(_BUILTIN_TEMPLATES_DIR, fname),
                        }
                    )

        # Custom templates
        if os.path.isdir(_CUSTOM_TEMPLATES_DIR):
            for fname in sorted(os.listdir(_CUSTOM_TEMPLATES_DIR)):
                if fname.endswith(".html"):
                    templates.append(
                        {
                            "name": fname.replace(".html", ""),
                            "type": "custom",
                            "path": os.path.join(_CUSTOM_TEMPLATES_DIR, fname),
                        }
                    )

        return templates

    def save_custom_template(self, name, content):
        """Save a custom template to disk.

        Args:
            name: Template name (without .html extension)
            content: HTML template content

        Returns:
            True on success, False on failure
        """
        os.makedirs(_CUSTOM_TEMPLATES_DIR, exist_ok=True)
        filename = f"{name}.html"
        filepath = os.path.join(_CUSTOM_TEMPLATES_DIR, filename)

        try:
            # Validate by rendering with empty context first
            self.env.from_string(content)
            with open(filepath, "w") as f:
                f.write(content)
            return True
        except Exception as e:
            logger.error("Failed to save template %s: %s", name, e)
            return False

    def delete_custom_template(self, name):
        """Delete a custom template.

        Args:
            name: Template name (without .html extension)

        Returns:
            True on success, False on failure
        """
        filename = f"{name}.html"
        filepath = os.path.join(_CUSTOM_TEMPLATES_DIR, filename)

        if os.path.exists(filepath):
            os.remove(filepath)
            return True
        return False

    def get_custom_template_content(self, name):
        """Get the raw content of a custom template.

        Returns:
            Template content string or None if not found
        """
        filename = f"{name}.html"
        filepath = os.path.join(_CUSTOM_TEMPLATES_DIR, filename)

        if os.path.exists(filepath):
            with open(filepath) as f:
                return f.read()
        return None


# Module-level singleton
_default_renderer = None


def _get_renderer():
    """Get or create the default renderer singleton."""
    global _default_renderer
    if _default_renderer is None:
        _default_renderer = EmailTemplateRenderer()
    return _default_renderer


def render_email(template_id, context=None):
    """Convenience function to render an email template.

    Args:
        template_id: Template identifier
        context: Dict of template variables

    Returns:
        Rendered HTML string
    """
    return _get_renderer().render(template_id, context)


def get_default_template():
    """Return the raw default template content."""
    return _get_renderer().get_custom_template_content("default") or _get_renderer().render("default", {})