"""
BizNode Support Helper
======================
Collects system diagnostics, logs, and service status for support tickets.
Can email bundled logs to BizNode support team.

Usage:
    from support_helper import collect_logs, send_support_email

    logs = collect_logs()
    send_support_email("Setup failed", "Docker won't start", attachments=[logs])

CLI:
    python support_helper.py collect          # Collect and display diagnostics
    python support_helper.py send "Subject"   # Collect + email to support
"""

from __future__ import annotations

import json
import logging
import os
import platform
import shutil
import smtplib
import socket
import subprocess
import sys
import tempfile
import time
import zipfile
from datetime import datetime, timedelta
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from pathlib import Path

_ROOT = Path(__file__).parent.resolve()
_LOG_DIR = _ROOT / "logs"
_NO_WINDOW = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0

SUPPORT_EMAIL = "biznodesupport@1bz.biz"
SMTP_HOST = "smtp.1bz.biz"
SMTP_PORT = 587
LOG_HOURS = 48

SUPPORT_DISCLAIMER = (
    "DISCLAIMER: By clicking 'Send to Support', you agree to send your BizNode "
    "installation logs and system diagnostics (last 48 hours) to the BizNode support "
    "team at biznodesupport@1bz.biz for observation and troubleshooting purposes only. "
    "Sensitive data (passwords, tokens, API keys) are automatically redacted before "
    "sending. BizNode™, 1BZ DZIT DAO LLC, and its affiliates accept NO RESPONSIBILITY "
    "for any loss, damage, or data exposure arising from the use of this support feature. "
    "By proceeding, you acknowledge and accept these terms."
)

log = logging.getLogger("biznode.support")


def _run(cmd: list, timeout: int = 10) -> str:
    try:
        kw = {"capture_output": True, "text": True, "timeout": timeout}
        if sys.platform == "win32":
            kw["creationflags"] = _NO_WINDOW
        r = subprocess.run(cmd, **kw)
        return r.stdout.strip()
    except Exception as e:
        return f"ERROR: {e}"


def _port_open(port: int) -> bool:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(2)
        return s.connect_ex(("127.0.0.1", port)) == 0


def _file_content(path: Path, max_lines: int = 500, hours: int = LOG_HOURS) -> str:
    try:
        if not path.exists():
            return f"[not found: {path}]"
        cutoff = datetime.now() - timedelta(hours=hours)
        lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
        filtered = []
        for line in lines:
            try:
                ts_str = line[:19]
                ts = datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S")
                if ts >= cutoff:
                    filtered.append(line)
            except (ValueError, IndexError):
                filtered.append(line)
        if len(filtered) > max_lines:
            filtered = filtered[-max_lines:]
        return "\n".join(filtered) if filtered else "\n".join(lines[-max_lines:])
    except Exception as e:
        return f"[error reading {path}: {e}]"


# ── System info ──────────────────────────────────────────────────────────────

def get_system_info() -> dict:
    info = {
        "timestamp": datetime.now().isoformat(),
        "platform": platform.platform(),
        "os_version": platform.version(),
        "architecture": platform.machine(),
        "processor": platform.processor(),
        "python_version": sys.version,
        "python_executable": sys.executable,
        "hostname": socket.gethostname(),
        "install_path": str(_ROOT),
    }

    try:
        import psutil
        info["ram_total_gb"] = round(psutil.virtual_memory().total / (1024 ** 3), 1)
        info["ram_available_gb"] = round(psutil.virtual_memory().available / (1024 ** 3), 1)
        info["cpu_count"] = psutil.cpu_count()
        info["disk_free_gb"] = round(psutil.disk_usage(str(_ROOT)).free / (1024 ** 3), 1)
        info["disk_total_gb"] = round(psutil.disk_usage(str(_ROOT)).total / (1024 ** 3), 1)
    except ImportError:
        if sys.platform == "win32":
            wmic_mem = _run(["wmic", "OS", "get", "FreePhysicalMemory,TotalVisibleMemorySize", "/value"])
            info["memory_wmic"] = wmic_mem
            wmic_cpu = _run(["wmic", "cpu", "get", "NumberOfLogicalProcessors", "/value"])
            info["cpu_wmic"] = wmic_cpu

    info["docker_version"] = _run(["docker", "--version"])
    info["ollama_version"] = _run(["ollama", "--version"])

    return info


def get_service_status() -> dict:
    status = {
        "ollama_api": _port_open(11434),
        "qdrant_api": _port_open(6333),
        "dashboard": _port_open(7777),
        "postgresql": _port_open(5432),
    }

    try:
        docker_info = _run(["docker", "info"], timeout=15)
        status["docker_running"] = "ERROR" not in docker_info and "Server" in docker_info
    except Exception:
        status["docker_running"] = False

    if sys.platform == "win32":
        tasklist = _run(["tasklist", "/FO", "CSV", "/NH"])
        for proc in ["ollama.exe", "qdrant.exe", "python.exe", "Docker Desktop.exe"]:
            status[f"process_{proc}"] = proc.lower() in tasklist.lower()

    return status


# ── Log collection ───────────────────────────────────────────────────────────

def collect_logs() -> str:
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    zip_name = f"biznode_support_{timestamp}.zip"
    zip_path = os.path.join(tempfile.gettempdir(), zip_name)

    with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
        zf.writestr("system_info.json", json.dumps(get_system_info(), indent=2, default=str))
        zf.writestr("service_status.json", json.dumps(get_service_status(), indent=2, default=str))

        zf.writestr("disclaimer.txt", SUPPORT_DISCLAIMER)
        zf.writestr("collection_info.txt", (
            f"Log collection period: last {LOG_HOURS} hours\n"
            f"Collected at: {datetime.now().isoformat()}\n"
            f"Cutoff: {(datetime.now() - timedelta(hours=LOG_HOURS)).isoformat()}\n"
        ))

        log_files = [
            _LOG_DIR / "install.log",
            _LOG_DIR / "bot.log",
            _LOG_DIR / "dashboard.log",
            _LOG_DIR / "watchdog.log",
            _LOG_DIR / "backup.log",
            _LOG_DIR / "scheduler.log",
            _LOG_DIR / "node_monitor" / "auto_heal.log",
        ]
        for p in _LOG_DIR.glob("*.log"):
            if p not in log_files:
                log_files.append(p)

        for lf in log_files:
            if lf.exists():
                content = _file_content(lf, max_lines=500, hours=LOG_HOURS)
                rel = lf.name if lf.parent == _LOG_DIR else f"{lf.parent.name}/{lf.name}"
                zf.writestr(f"logs/{rel}", content)

        env_file = _ROOT / ".env"
        if env_file.exists():
            env_lines = []
            for line in env_file.read_text(encoding="utf-8", errors="replace").splitlines():
                stripped = line.strip()
                if "=" in stripped and not stripped.startswith("#"):
                    key = stripped.split("=", 1)[0]
                    sensitive_keys = ["PASSWORD", "SECRET", "TOKEN", "KEY", "API_KEY", "PRIVATE"]
                    if any(s in key.upper() for s in sensitive_keys):
                        env_lines.append(f"{key}=***REDACTED***")
                        continue
                env_lines.append(line)
            zf.writestr("env_redacted.txt", "\n".join(env_lines))

        reg_file = _ROOT / ".registration"
        if reg_file.exists():
            try:
                reg = json.loads(reg_file.read_text(encoding="utf-8"))
                if "email" in reg:
                    reg["email"] = reg["email"][:3] + "***"
                zf.writestr("registration.json", json.dumps(reg, indent=2))
            except Exception:
                pass

        version_file = _ROOT / "VERSION"
        if version_file.exists():
            zf.writestr("VERSION", version_file.read_text(encoding="utf-8"))

    log.info(f"Support bundle created: {zip_path}")
    return zip_path


# ── Email ────────────────────────────────────────────────────────────────────

def send_support_email(
    subject: str,
    body: str,
    attachments: list[str] | None = None,
    from_email: str | None = None,
    smtp_host: str | None = None,
    smtp_port: int | None = None,
    smtp_user: str | None = None,
    smtp_pass: str | None = None,
) -> bool:
    env_file = _ROOT / ".env"
    if env_file.exists():
        for line in env_file.read_text(encoding="utf-8", errors="replace").splitlines():
            line = line.strip()
            if line.startswith("SMTP_HOST=") and not smtp_host:
                smtp_host = line.split("=", 1)[1].strip()
            elif line.startswith("SMTP_PORT=") and not smtp_port:
                try:
                    smtp_port = int(line.split("=", 1)[1].strip())
                except ValueError:
                    pass
            elif line.startswith("SMTP_USER=") and not smtp_user:
                smtp_user = line.split("=", 1)[1].strip()
            elif line.startswith("SMTP_PASSWORD=") and not smtp_pass:
                smtp_pass = line.split("=", 1)[1].strip()
            elif line.startswith("SUPPORT_FROM_EMAIL=") and not from_email:
                from_email = line.split("=", 1)[1].strip()

    smtp_host = smtp_host or SMTP_HOST
    smtp_port = smtp_port or SMTP_PORT
    from_email = from_email or smtp_user or f"biznode@{socket.gethostname()}"

    reg_file = _ROOT / ".registration"
    reg_id = "unknown"
    if reg_file.exists():
        try:
            reg = json.loads(reg_file.read_text(encoding="utf-8"))
            reg_id = reg.get("registration_id", "unknown")
        except Exception:
            pass

    full_subject = f"[BizNode Support] {subject} (Reg: {reg_id})"

    msg = MIMEMultipart()
    msg["From"] = from_email
    msg["To"] = SUPPORT_EMAIL
    msg["Subject"] = full_subject

    system_info = get_system_info()
    footer = (
        f"\n\n---\n"
        f"Registration: {reg_id}\n"
        f"Platform: {system_info.get('platform', 'unknown')}\n"
        f"Python: {system_info.get('python_version', 'unknown')}\n"
        f"Install path: {_ROOT}\n"
        f"Timestamp: {datetime.now().isoformat()}\n"
    )
    msg.attach(MIMEText(body + footer, "plain"))

    if attachments:
        for filepath in attachments:
            if not os.path.exists(filepath):
                continue
            with open(filepath, "rb") as f:
                part = MIMEApplication(f.read(), Name=os.path.basename(filepath))
            part["Content-Disposition"] = f'attachment; filename="{os.path.basename(filepath)}"'
            msg.attach(part)

    try:
        if smtp_port == 465:
            server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=30)
        else:
            server = smtplib.SMTP(smtp_host, smtp_port, timeout=30)
            server.ehlo()
            try:
                server.starttls()
                server.ehlo()
            except smtplib.SMTPNotSupportedError:
                pass

        if smtp_user and smtp_pass:
            server.login(smtp_user, smtp_pass)

        server.sendmail(from_email, [SUPPORT_EMAIL], msg.as_string())
        server.quit()
        log.info(f"Support email sent to {SUPPORT_EMAIL}")
        return True

    except Exception as e:
        log.error(f"Failed to send support email: {e}")
        log.info("Email could not be sent. Please manually email the support bundle to:")
        log.info(f"  {SUPPORT_EMAIL}")
        return False


# ── Dashboard integration point ──────────────────────────────────────────────

def get_support_dashboard_data() -> dict:
    return {
        "system_info": get_system_info(),
        "service_status": get_service_status(),
        "support_email": SUPPORT_EMAIL,
        "log_file": str(_LOG_DIR / "install.log"),
        "log_hours": LOG_HOURS,
        "disclaimer": SUPPORT_DISCLAIMER,
        "can_send_email": bool(
            (_ROOT / ".env").exists()
            and any(
                "SMTP_" in line
                for line in (_ROOT / ".env").read_text(encoding="utf-8", errors="replace").splitlines()
            )
        ) if (_ROOT / ".env").exists() else False,
    }


# ── CLI ──────────────────────────────────────────────────────────────────────

def main():
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s [%(levelname)s] %(message)s",
    )

    if len(sys.argv) < 2:
        print("Usage:")
        print("  python support_helper.py collect              Collect diagnostics zip")
        print('  python support_helper.py send "Subject"       Collect + email to support')
        print("  python support_helper.py status               Show service status")
        sys.exit(0)

    action = sys.argv[1].lower()

    if action == "collect":
        print("Collecting diagnostic information...")
        zip_path = collect_logs()
        print(f"\nSupport bundle created: {zip_path}")
        print(f"Email this file to {SUPPORT_EMAIL} for assistance.")

    elif action == "send":
        subject = sys.argv[2] if len(sys.argv) > 2 else "Support Request"
        body = sys.argv[3] if len(sys.argv) > 3 else "Please see attached diagnostics."

        print("\n" + "=" * 60)
        print("  IMPORTANT DISCLAIMER")
        print("=" * 60)
        print(f"\n{SUPPORT_DISCLAIMER}\n")
        confirm = input("Do you agree and wish to proceed? (yes/no): ").strip().lower()
        if confirm not in ("yes", "y"):
            print("Cancelled. No data was sent.")
            sys.exit(0)

        print(f"\nCollecting last {LOG_HOURS} hours of diagnostic information...")
        zip_path = collect_logs()

        print(f"Sending to {SUPPORT_EMAIL}...")
        ok = send_support_email(subject, body, attachments=[zip_path])
        if ok:
            print("Support email sent successfully!")
        else:
            print(f"\nCould not send email automatically.")
            print(f"Please email the support bundle manually:")
            print(f"  File: {zip_path}")
            print(f"  To:   {SUPPORT_EMAIL}")

    elif action == "status":
        print("\n" + "=" * 50)
        print("  BizNode Service Status")
        print("=" * 50)
        status = get_service_status()
        for name, value in status.items():
            label = name.replace("_", " ").title()
            indicator = "[OK]" if value else "[--]"
            print(f"  {indicator} {label}")
        print("=" * 50)

        print("\n  System Information:")
        info = get_system_info()
        for key in ["platform", "python_version", "ram_total_gb", "disk_free_gb", "docker_version", "ollama_version"]:
            if key in info:
                label = key.replace("_", " ").title()
                print(f"    {label}: {info[key]}")
        print()

    else:
        print(f"Unknown action: {action}")
        sys.exit(1)


if __name__ == "__main__":
    main()
