import smtplib, ssl, threading
from email.message import EmailMessage
from datetime import datetime
from flask import current_app
from jinja2 import Environment, FileSystemLoader, select_autoescape

try:
    from models import db, EmailLog  # optional
except Exception:
    db = EmailLog = None

_env = Environment(
    loader=FileSystemLoader("templates/emails"),
    autoescape=select_autoescape(["html", "xml"])
)

def _brand_defaults():
    cfg = current_app.config
    return {
        "brand": {
            "name": cfg.get("BRAND_NAME", "CHL HRMS"),
            "primary_color": cfg.get("BRAND_PRIMARY_COLOR", "#0d6efd"),  # bootstrap primary
            "logo_url": cfg.get("BRAND_LOGO_URL", ""),
            "address": cfg.get("BRAND_ADDRESS", ""),
            "footer_text": cfg.get("BRAND_FOOTER_TEXT", "This is an automated message. Please do not reply."),
            "website_url": cfg.get("BRAND_WEBSITE_URL", ""),
        },
        # Back-compat keys if templates reference directly
        "company_name": cfg.get("BRAND_NAME", "CHL HRMS"),
    }

def render_template_pair(template_name: str, ctx: dict):
    # Merge branding defaults but allow overrides in ctx
    merged = {**_brand_defaults(), **(ctx or {})}

    subject_tmpl = _env.get_template(f"{template_name}.subject.txt")
    html_tmpl    = _env.get_template(f"{template_name}.html")
    # text is optional
    text_name = f"{template_name}.txt"
    text_tmpl = _env.get_template(text_name) if text_name in _env.list_templates() else None

    subject = subject_tmpl.render(**merged).strip()
    html    = html_tmpl.render(**merged)
    text    = text_tmpl.render(**merged) if text_tmpl else None
    return subject, html, text

def _send_smtp(subject, to, html, text=None, attachments=None):
    cfg = current_app.config
    msg = EmailMessage()
    msg["Subject"] = subject
    msg["From"]    = cfg.get("MAIL_FROM")
    msg["To"]      = to

    if text and html:
        msg.set_content(text)
        msg.add_alternative(html, subtype="html")
    elif html:
        msg.add_alternative(html, subtype="html")
    else:
        msg.set_content(text or "")

    for att in (attachments or []):
        maintype, subtype = (att.get("mime","application/octet-stream").split("/",1)+["octet-stream"])[:2]
        msg.add_attachment(att["content"], maintype=maintype, subtype=subtype, filename=att["filename"])

    context = ssl.create_default_context()
    with smtplib.SMTP(cfg["MAIL_SERVER"], cfg["MAIL_PORT"]) as server:
        if cfg.get("MAIL_USE_TLS", True):
            server.starttls(context=context)
        server.login(cfg["MAIL_USERNAME"], cfg["MAIL_PASSWORD"])
        server.send_message(msg)

def _log_email(to, template, subject, status, error=None):
    if not (db and EmailLog):
        return
    try:
        rec = EmailLog(
            to_email=to, template=template, subject=subject,
            status=status, error=error, created_at=datetime.utcnow()
        )
        db.session.add(rec)
        db.session.commit()
    except Exception:
        db.session.rollback()

class EmailService:
    @staticmethod
    def send(template: str, to: str, ctx: dict, attachments=None, async_send=True):
        subject, html, text = render_template_pair(template, ctx or {})

        # capture the real app object for use inside the thread
        app = current_app._get_current_object()

        def _work():
            # 🔴 IMPORTANT: push app context so db.session is usable
            with app.app_context():
                try:
                    _send_smtp(subject, to, html, text, attachments)
                    _log_email(to, template, subject, "sent")
                except Exception as e:
                    _log_email(to, template, subject, "failed", str(e))

        if async_send:
            threading.Thread(target=_work, daemon=True).start()
        else:
            _work()