"""
BizNode Support Email Service
==============================
Monitors biznodesupport@1bz.biz for incoming support emails,
auto-detects common installation/setup issues from log content,
sends auto-replies, and forwards unresolved tickets to the owner via Telegram.
"""

import email
import imaplib
import io
import json
import logging
import os
import re
import smtplib
import subprocess
import sys
import tempfile
import time
import zipfile
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from pathlib import Path
from typing import Optional

log = logging.getLogger("biznode.support_email")
_PROJECT_ROOT = Path(__file__).resolve().parent.parent
_NO_WINDOW = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0

IMAP_HOST = os.getenv("SUPPORT_IMAP_HOST", "10.50.0.1")
IMAP_PORT = 993
SMTP_HOST = os.getenv("SUPPORT_SMTP_HOST", "10.50.0.1")
SMTP_PORT = 465
SUPPORT_EMAIL = "biznodesupport@1bz.biz"
POLL_INTERVAL = 300

ISSUE_PATTERNS = {
    "docker_not_found": {
        "patterns": [
            r"docker\s+(not\s+found|is\s+not\s+recognized|command\s+not\s+found)",
            r"'docker'\s+is\s+not\s+recognized",
            r"cannot\s+find.*docker",
        ],
        "category": "Docker Not Found",
        "priority": "high",
        "response_subject": "BizNode Support: Docker Installation Guide",
        "response_body": (
            "Hello,\n\n"
            "It looks like Docker is not installed on your system. Here's how to fix this:\n\n"
            "1. Download Docker Desktop from: https://www.docker.com/products/docker-desktop/\n"
            "2. Run the installer and follow the on-screen instructions\n"
            "3. After installation, restart your computer\n"
            "4. Open a terminal and verify with: docker --version\n"
            "5. Make sure Docker Desktop is running (check the system tray icon)\n"
            "6. Then restart BizNode with: python service_manager.py start\n\n"
            "If you continue to have issues, please reply to this email with any error messages.\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
    "ollama_connection": {
        "patterns": [
            r"ollama.*connection\s+refused",
            r"connection\s+refused.*ollama",
            r"failed\s+to\s+connect.*ollama",
            r"ollama.*not\s+running",
            r"port\s+11434.*refused",
        ],
        "category": "Ollama Connection Issue",
        "priority": "high",
        "response_subject": "BizNode Support: Ollama Setup Steps",
        "response_body": (
            "Hello,\n\n"
            "It appears Ollama is not running or not accessible. Follow these steps:\n\n"
            "1. Download Ollama from: https://ollama.com/download\n"
            "2. Install it and make sure it's running (check system tray on Windows)\n"
            "3. Open a terminal and run: ollama serve\n"
            "4. Pull the required model: ollama pull qwen3.5:4b\n"
            "5. Verify it's working: ollama list\n"
            "6. Make sure port 11434 is not blocked by your firewall\n"
            "7. Restart BizNode: python service_manager.py restart\n\n"
            "If Ollama was already installed, try restarting it:\n"
            "  - Close Ollama from the system tray\n"
            "  - Open a new terminal and run: ollama serve\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
    "port_in_use": {
        "patterns": [
            r"port.*already\s+in\s+use",
            r"address\s+already\s+in\s+use",
            r"bind.*EADDRINUSE",
            r"port\s+\d+.*occupied",
        ],
        "category": "Port Conflict",
        "priority": "normal",
        "response_subject": "BizNode Support: Port Conflict Resolution",
        "response_body": (
            "Hello,\n\n"
            "A port conflict was detected. Here's how to resolve it:\n\n"
            "On Windows:\n"
            "1. Open Command Prompt as Administrator\n"
            "2. Find what's using the port: netstat -ano | findstr :PORT_NUMBER\n"
            "3. Kill the process: taskkill /PID <PID> /F\n"
            "4. Alternatively, change BizNode's port in your .env file\n\n"
            "Common BizNode ports:\n"
            "  - 7777: Dashboard API\n"
            "  - 6333: Qdrant vector DB\n"
            "  - 11434: Ollama LLM\n\n"
            "After freeing the port, restart BizNode: python service_manager.py restart\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
    "postgresql_failed": {
        "patterns": [
            r"postgresql.*connection\s+failed",
            r"psycopg2.*connection\s+refused",
            r"could\s+not\s+connect.*postgresql",
            r"database.*connection.*failed",
            r"FATAL.*password\s+authentication\s+failed",
            r"pg_hba\.conf",
        ],
        "category": "PostgreSQL Connection Failed",
        "priority": "high",
        "response_subject": "BizNode Support: Database Setup Guide",
        "response_body": (
            "Hello,\n\n"
            "Your BizNode instance is having trouble connecting to PostgreSQL. Here's the fix:\n\n"
            "Option A: Use SQLite (simplest, no setup needed):\n"
            "  1. In your .env file, set: DB_TYPE=sqlite\n"
            "  2. Restart BizNode: python service_manager.py restart\n\n"
            "Option B: Fix PostgreSQL connection:\n"
            "  1. Make sure PostgreSQL is installed and running\n"
            "  2. Create the database: createdb biznode\n"
            "  3. Create the user: createuser biznode_user -P\n"
            "  4. Grant permissions: GRANT ALL ON DATABASE biznode TO biznode_user;\n"
            "  5. Update your .env file with the correct credentials:\n"
            "     DB_TYPE=postgresql\n"
            "     DB_HOST=localhost\n"
            "     DB_PORT=5432\n"
            "     DB_NAME=biznode\n"
            "     DB_USER=biznode_user\n"
            "     DB_PASSWORD=your_password\n"
            "  6. Restart BizNode: python service_manager.py restart\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
    "permission_denied": {
        "patterns": [
            r"permission\s+denied",
            r"access\s+is\s+denied",
            r"requires?\s+admin",
            r"elevation\s+required",
            r"run\s+as\s+administrator",
        ],
        "category": "Permission Denied",
        "priority": "normal",
        "response_subject": "BizNode Support: Run as Administrator",
        "response_body": (
            "Hello,\n\n"
            "It looks like BizNode needs elevated permissions. Here's how to fix this:\n\n"
            "On Windows:\n"
            "1. Right-click on Command Prompt or PowerShell\n"
            "2. Select 'Run as administrator'\n"
            "3. Navigate to your BizNode folder\n"
            "4. Run: python service_manager.py start\n\n"
            "If you installed Python for 'All Users', also make sure the BizNode folder\n"
            "is not in a protected system directory (like Program Files).\n\n"
            "Recommended: Install BizNode in a user-writable location like:\n"
            "  C:\\BizNode\\ or C:\\Users\\YourName\\BizNode\\\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
    "module_not_found": {
        "patterns": [
            r"ModuleNotFoundError",
            r"ImportError",
            r"No\s+module\s+named",
            r"module\s+not\s+found",
            r"import\s+error",
        ],
        "category": "Missing Python Module",
        "priority": "normal",
        "response_subject": "BizNode Support: Install Missing Dependencies",
        "response_body": (
            "Hello,\n\n"
            "Your BizNode installation is missing some Python packages. Here's how to fix it:\n\n"
            "1. Open a terminal in your BizNode directory\n"
            "2. Run: pip install -r requirements.txt\n"
            "3. If a specific module is missing, install it directly:\n"
            "   pip install <module_name>\n\n"
            "Common modules that may need manual installation:\n"
            "  pip install psycopg2-binary    # PostgreSQL\n"
            "  pip install python-telegram-bot # Telegram bot\n"
            "  pip install fastapi uvicorn     # Dashboard\n"
            "  pip install qdrant-client       # Vector DB\n"
            "  pip install requests            # HTTP client\n\n"
            "After installing, restart BizNode: python service_manager.py restart\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
    "license_invalid": {
        "patterns": [
            r"license\s+invalid",
            r"invalid\s+license",
            r"license\s+key.*not\s+(valid|found|recognized)",
            r"license\s+expired",
            r"activation\s+failed",
            r"license\s+error",
        ],
        "category": "License Issue",
        "priority": "high",
        "response_subject": "BizNode Support: License Activation Steps",
        "response_body": (
            "Hello,\n\n"
            "There's an issue with your BizNode license. Here's how to resolve it:\n\n"
            "1. Check your license key format: BZN-XXXXX-XXXXX-XXXXX\n"
            "2. Make sure you entered it correctly in the dashboard Settings page\n"
            "3. Verify your license is active at: https://biznode.1bz.biz/admin/login.php\n\n"
            "If you recently purchased:\n"
            "  - Check your email for the license key (from biznodesupport@1bz.biz)\n"
            "  - It may take a few minutes for Stripe purchases to process\n"
            "  - For crypto purchases, verify your transaction was confirmed on-chain\n\n"
            "To re-activate:\n"
            "  1. Open the BizNode Dashboard at http://localhost:7777\n"
            "  2. Go to Settings > License\n"
            "  3. Enter your license key and click Activate\n\n"
            "If the problem persists, reply with your license key and purchase email,\n"
            "and we'll manually verify your license.\n\n"
            "Best regards,\nBizNode Support Team"
        ),
    },
}


def _get_password() -> str:
    admin_env = _PROJECT_ROOT / "tools" / "admin" / "admin.env"
    if admin_env.exists():
        for line in admin_env.read_text(encoding="utf-8").splitlines():
            line = line.strip()
            if line.startswith("SUPPORT_EMAIL_PASSWORD="):
                return line.split("=", 1)[1].strip()
    return os.getenv("SUPPORT_EMAIL_PASSWORD", "")


def _get_telegram_token() -> str:
    env_file = _PROJECT_ROOT / ".env"
    if env_file.exists():
        for line in env_file.read_text(encoding="utf-8").splitlines():
            if line.strip().startswith("TELEGRAM_BOT_TOKEN="):
                return line.strip().split("=", 1)[1].strip()
    return os.getenv("TELEGRAM_BOT_TOKEN", "")


def _get_db():
    import importlib.util
    spec = importlib.util.spec_from_file_location("_db", str(_PROJECT_ROOT / "memory" / "database.py"))
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod


def _detect_issue(text: str) -> Optional[dict]:
    text_lower = text.lower()
    for issue_key, issue_info in ISSUE_PATTERNS.items():
        for pattern in issue_info["patterns"]:
            if re.search(pattern, text_lower, re.IGNORECASE):
                return {
                    "key": issue_key,
                    "category": issue_info["category"],
                    "priority": issue_info["priority"],
                    "response_subject": issue_info["response_subject"],
                    "response_body": issue_info["response_body"],
                }
    return None


def _extract_text_from_email(msg: email.message.Message) -> tuple[str, list[str]]:
    body = ""
    attachments = []
    if msg.is_multipart():
        for part in msg.walk():
            content_type = part.get_content_type()
            disposition = str(part.get("Content-Disposition", ""))
            if "attachment" in disposition:
                filename = part.get_filename() or "attachment"
                attachments.append(filename)
                if filename.endswith(".zip"):
                    try:
                        payload = part.get_payload(decode=True)
                        if payload:
                            body += _extract_zip_logs(payload)
                    except Exception as exc:
                        log.warning("Failed to extract zip: %s", exc)
                elif filename.endswith((".log", ".txt")):
                    try:
                        payload = part.get_payload(decode=True)
                        if payload:
                            body += "\n\n--- Attachment: " + filename + " ---\n"
                            body += payload.decode("utf-8", errors="replace")
                    except Exception:
                        pass
            elif content_type == "text/plain":
                payload = part.get_payload(decode=True)
                if payload:
                    body += payload.decode("utf-8", errors="replace")
    else:
        payload = msg.get_payload(decode=True)
        if payload:
            body = payload.decode("utf-8", errors="replace")
    return body, attachments


def _extract_zip_logs(zip_data: bytes) -> str:
    text = ""
    try:
        with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
            for name in zf.namelist():
                if name.endswith((".log", ".txt", ".err", ".out")):
                    try:
                        with zf.open(name) as f:
                            content = f.read().decode("utf-8", errors="replace")
                            text += f"\n\n--- Log: {name} ---\n{content[:5000]}"
                    except Exception:
                        pass
    except Exception as exc:
        log.warning("Failed to read zip: %s", exc)
    return text


def _send_reply(to_addr: str, subject: str, body: str) -> bool:
    password = _get_password()
    if not password:
        log.error("SUPPORT_EMAIL_PASSWORD not configured")
        return False
    try:
        msg = MIMEMultipart()
        msg["From"] = SUPPORT_EMAIL
        msg["To"] = to_addr
        msg["Subject"] = subject
        msg.attach(MIMEText(body, "plain"))

        with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=30) as server:
            server.login(SUPPORT_EMAIL, password)
            server.sendmail(SUPPORT_EMAIL, to_addr, msg.as_string())
        log.info("Auto-reply sent to %s: %s", to_addr, subject)
        return True
    except Exception as exc:
        log.error("Failed to send reply to %s: %s", to_addr, exc)
        return False


def send_manual_reply(ticket_id: int, reply_body: str) -> bool:
    db = _get_db()
    conn = db.get_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT email, subject FROM support_tickets WHERE id = ?", (ticket_id,))
    row = cursor.fetchone()
    conn.close()
    if not row:
        return False

    to_addr = row["email"]
    subject = f"Re: {row['subject'] or 'Your Support Request'}"
    if _send_reply(to_addr, subject, reply_body):
        conn = db.get_connection()
        cursor = conn.cursor()
        cursor.execute(
            "UPDATE support_tickets SET status = ?, auto_response = COALESCE(auto_response, '') || ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
            ("replied", f"\n\n--- Admin Reply ---\n{reply_body}", ticket_id),
        )
        conn.commit()
        conn.close()
        return True
    return False


def _forward_to_telegram(ticket_id: int, sender: str, subject: str, body_preview: str, category: str):
    token = _get_telegram_token()
    chat_id = "7668967550"
    if not token:
        log.warning("No TELEGRAM_BOT_TOKEN for forwarding support ticket")
        return
    try:
        import requests
        text = (
            f"*New Support Ticket #{ticket_id}*\n\n"
            f"From: {sender}\n"
            f"Subject: {subject}\n"
            f"Category: {category or 'Undetected'}\n\n"
            f"{body_preview[:500]}"
        )
        requests.post(
            f"https://api.telegram.org/bot{token}/sendMessage",
            json={"chat_id": chat_id, "text": text, "parse_mode": "Markdown"},
            timeout=10,
        )
    except Exception as exc:
        log.warning("Failed to forward ticket to Telegram: %s", exc)


def create_ticket(email_addr: str, subject: str, body: str,
                  attachments: str = "", issue_category: str = "",
                  auto_response: str = "", status: str = "open",
                  priority: str = "normal") -> int:
    db = _get_db()
    conn = db.get_connection()
    cursor = conn.cursor()
    cursor.execute(
        """INSERT INTO support_tickets
           (email, subject, body, attachments, issue_category,
            auto_response, status, priority)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
        (email_addr, subject, body[:10000], attachments, issue_category,
         auto_response, status, priority),
    )
    ticket_id = cursor.lastrowid
    conn.commit()
    conn.close()
    return ticket_id


def get_ticket(ticket_id: int) -> Optional[dict]:
    db = _get_db()
    conn = db.get_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM support_tickets WHERE id = ?", (ticket_id,))
    row = cursor.fetchone()
    if not row:
        conn.close()
        return None
    ticket = dict(row)
    try:
        cursor.execute(
            "SELECT * FROM support_ticket_replies WHERE ticket_id = ? ORDER BY created_at ASC",
            (ticket_id,),
        )
        ticket["replies"] = [dict(r) for r in cursor.fetchall()]
    except Exception:
        ticket["replies"] = []
    conn.close()
    return ticket


def get_tickets(status: str = "", limit: int = 50, offset: int = 0) -> list:
    db = _get_db()
    conn = db.get_connection()
    cursor = conn.cursor()
    if status:
        cursor.execute(
            "SELECT * FROM support_tickets WHERE status = ? ORDER BY created_at DESC LIMIT ? OFFSET ?",
            (status, limit, offset),
        )
    else:
        cursor.execute(
            "SELECT * FROM support_tickets ORDER BY created_at DESC LIMIT ? OFFSET ?",
            (limit, offset),
        )
    rows = cursor.fetchall()
    conn.close()
    return rows


def get_stats() -> dict:
    db = _get_db()
    conn = db.get_connection()
    cursor = conn.cursor()

    cursor.execute("SELECT COUNT(*) as total FROM support_tickets")
    total = cursor.fetchone()["total"]

    cursor.execute("SELECT COUNT(*) as c FROM support_tickets WHERE status = 'open'")
    open_count = cursor.fetchone()["c"]

    cursor.execute("SELECT COUNT(*) as c FROM support_tickets WHERE status = 'auto_resolved'")
    auto_resolved = cursor.fetchone()["c"]

    cursor.execute("SELECT COUNT(*) as c FROM support_tickets WHERE status IN ('resolved', 'auto_resolved', 'replied')")
    resolved = cursor.fetchone()["c"]

    cursor.execute("SELECT COUNT(*) as c FROM support_tickets WHERE status = 'forwarded'")
    forwarded = cursor.fetchone()["c"]

    cursor.execute(
        "SELECT issue_category, COUNT(*) as c FROM support_tickets "
        "WHERE issue_category IS NOT NULL AND issue_category != '' "
        "GROUP BY issue_category ORDER BY c DESC LIMIT 10"
    )
    common_issues = cursor.fetchall()

    cursor.execute(
        "SELECT COUNT(*) as c FROM support_tickets "
        "WHERE created_at >= date('now', '-1 day')"
    )
    today = cursor.fetchone()["c"]

    conn.close()

    resolution_rate = round((resolved / total * 100), 1) if total > 0 else 0.0

    return {
        "total": total,
        "open": open_count,
        "auto_resolved": auto_resolved,
        "resolved": resolved,
        "forwarded": forwarded,
        "today": today,
        "resolution_rate": resolution_rate,
        "common_issues": common_issues,
    }


def update_ticket(ticket_id: int, **kwargs) -> bool:
    if not kwargs:
        return False
    db = _get_db()
    conn = db.get_connection()
    cursor = conn.cursor()
    parts = []
    vals = []
    for k, v in kwargs.items():
        parts.append(f"{k} = ?")
        vals.append(v)
    parts.append("updated_at = CURRENT_TIMESTAMP")
    vals.append(ticket_id)
    cursor.execute(f"UPDATE support_tickets SET {', '.join(parts)} WHERE id = ?", vals)
    conn.commit()
    success = cursor.rowcount > 0
    conn.close()
    return success


def process_email(msg: email.message.Message) -> int:
    sender = email.utils.parseaddr(msg.get("From", ""))[1]
    subject = msg.get("Subject", "(No Subject)")

    body, attachments = _extract_text_from_email(msg)
    full_text = f"{subject}\n\n{body}"

    issue = _detect_issue(full_text)

    if issue:
        category = issue["category"]
        priority = issue["priority"]
        auto_response = issue["response_body"]
        _send_reply(sender, issue["response_subject"], auto_response)
        status = "auto_resolved"
    else:
        category = ""
        priority = "normal"
        auto_response = ""
        status = "forwarded"

    ticket_id = create_ticket(
        email_addr=sender,
        subject=subject,
        body=body[:10000],
        attachments=json.dumps(attachments) if attachments else "",
        issue_category=category,
        auto_response=auto_response,
        status=status,
        priority=priority,
    )

    if status == "forwarded":
        _forward_to_telegram(ticket_id, sender, subject, body[:500], category)

    log.info(
        "Ticket #%d created — from=%s category=%s status=%s",
        ticket_id, sender, category or "undetected", status,
    )
    return ticket_id


def check_inbox() -> dict:
    password = _get_password()
    if not password:
        return {"error": "SUPPORT_EMAIL_PASSWORD not configured", "processed": 0}

    processed = 0
    errors = []
    try:
        mail = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT, timeout=30)
        mail.login(SUPPORT_EMAIL, password)
        mail.select("INBOX")

        status, data = mail.search(None, "UNSEEN")
        if status != "OK":
            mail.logout()
            return {"error": "IMAP search failed", "processed": 0}

        email_ids = data[0].split()
        for eid in email_ids:
            try:
                status, msg_data = mail.fetch(eid, "(RFC822)")
                if status != "OK":
                    continue
                raw = msg_data[0][1]
                msg = email.message_from_bytes(raw)
                process_email(msg)
                mail.store(eid, "+FLAGS", "\\Seen")
                processed += 1
            except Exception as exc:
                log.error("Failed to process email %s: %s", eid, exc)
                errors.append(str(exc))

        mail.logout()
    except Exception as exc:
        log.error("IMAP connection failed: %s", exc)
        return {"error": str(exc), "processed": processed}

    return {"processed": processed, "errors": errors}


def run_poll_loop():
    log.info("Support email monitor started — polling every %ds", POLL_INTERVAL)
    while True:
        try:
            result = check_inbox()
            if result.get("processed"):
                log.info("Processed %d support emails", result["processed"])
            if result.get("error"):
                log.warning("Inbox check error: %s", result["error"])
        except Exception as exc:
            log.error("Poll loop error: %s", exc)
        time.sleep(POLL_INTERVAL)


if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
    )
    run_poll_loop()
