"""
summary.py — Startup system status summary.
Runs automatically on launch and prints a server health overview.
"""

import os
import re
import subprocess
from datetime import date, datetime

from modules.output import good, good2, bad, bad2, warn, sout, header, info, svc_ok, svc_stopped
from modules.utils import (
    get_rhel_version, get_os_release, get_hostname, get_primary_ip,
    get_vz_ctid, is_cpanel, php_eol, date_check, run, require_bin
)

from modules.utils import VERSION as _VER
VERSION = f'GreenGeeks VPS Tool v{_VER}'


def run_summary():
    """Print the full startup system status summary."""
    os_ver = get_rhel_version()
    ctid = get_vz_ctid()

    # ── Version Header ────────────────────────────────────────────────────────
    print(f"[{VERSION}]  Container: {ctid}")

    # ── Operating System ──────────────────────────────────────────────────────
    os_release = get_os_release()
    print("[Operating System]")
    if os_ver and os_ver >= 8:
        good(os_release)
    else:
        bad(os_release)

    # ── Hostname ──────────────────────────────────────────────────────────────
    hostname = get_hostname()
    print("[Hostname]")
    if '.' not in hostname:
        bad2(f"{hostname} — Invalid Hostname (no dot in FQDN)")
    else:
        # Check DNS resolution
        dns_ok = _check_hostname_dns(hostname)
        if not dns_ok:
            bad2(f"{hostname} — DNS Not Setup")
        else:
            good2(hostname)

    # ── Primary IP ────────────────────────────────────────────────────────────
    ip = get_primary_ip()
    sout("Primary IP", ip)

    # ── cPanel Version ────────────────────────────────────────────────────────
    print("[cPanel Version]")
    _check_cpanel_version()

    # ── EasyApache ────────────────────────────────────────────────────────────
    print("[EasyApache]")
    _check_easyapache(os_ver)

    # ── Database ──────────────────────────────────────────────────────────────
    print("[Database]")
    _check_database()

    # ── Backup Status ─────────────────────────────────────────────────────────
    print("[Backups]")
    _check_backups()

    # ── Service Status ────────────────────────────────────────────────────────
    print("[Service Status]")
    _check_services(os_ver)

    # ── Server Health: Disk / Memory / Load ───────────────────────────────────
    print("[Server Health]  Disk Usage          Memory              Load")
    _check_server_health(os_ver)

    # ── Exim Queue ────────────────────────────────────────────────────────────
    print("[Exim Check]")
    _check_exim_queue()

    # Read SourceIPCheck value for sec-enable/sec-disable state tracking
    secpol = 'N'
    _config = '/var/cpanel/cpanel.config'
    if os.path.isfile(_config):
        with open(_config, 'r') as _f:
            _m = re.search(r'SecurityPolicy::SourceIPCheck=(\d)', _f.read())
            if _m:
                secpol = _m.group(1)
    return secpol


# ── Internal helpers ──────────────────────────────────────────────────────────

def _fmt_good(msg):
    from modules.output import COLORAMA_AVAILABLE
    if COLORAMA_AVAILABLE:
        from colorama import Fore, Style
        return Fore.GREEN + f"\u2714 {msg}" + Style.RESET_ALL
    return f"[OK] {msg}"


def _fmt_bad(msg):
    from modules.output import COLORAMA_AVAILABLE
    if COLORAMA_AVAILABLE:
        from colorama import Fore, Style
        return Fore.RED + f"X {msg}" + Style.RESET_ALL
    return f"[FAIL] {msg}"


def _check_hostname_dns(hostname):
    """Return True if hostname resolves via dig against 8.8.8.8."""
    if not require_bin('dig', 'hostname DNS check'):
        return False
    out, _, rc = run(['dig', '+noall', '+answer', hostname, '@8.8.8.8'])
    return bool(out.strip())


def _check_cpanel_version():
    version_file = '/usr/local/cpanel/version'
    if not os.path.isfile(version_file):
        warn("cPanel version file not found.")
        return

    with open(version_file, 'r') as f:
        raw = f.read().strip()

    # cPanel uses two formats: old "11.118.0.x" and new "134.0.x"
    m = re.match(r'^11\.(\d+)\.', raw)
    if m:
        cpver = raw[3:]          # strip leading "11."
        cpmajor = int(m.group(1))
    else:
        cpver = raw
        parts = raw.split('.')
        cpmajor = int(parts[0]) if parts[0].isdigit() else 0

    if cpmajor >= 118:
        good(cpver)
    elif cpmajor == 116:
        info(cpver)
    elif cpmajor in (102, 110, 114):
        bad(cpver)
    else:
        bad2(cpver)


def _check_easyapache(os_ver):
    httpd = '/usr/local/apache/bin/httpd'
    if not os.path.isfile(httpd):
        warn("Apache binary not found — skipping EasyApache check.")
        return

    out, _, _ = run([httpd, '-v'])
    ea_ver = 4  # default assumption
    for line in out.splitlines():
        if 'Easy' in line:
            m = re.search(r'v(\d+)', line)
            if m and m.group(1) == '3':
                ea_ver = 3
            break

    if ea_ver == 4:
        good("Version 4")
        _get_ea4_php()
    else:
        bad("Version 3")
        _get_ea3_php()


def _get_ea4_php():
    rebuild = '/usr/local/cpanel/bin/rebuild_phpconf'
    if not os.path.isfile(rebuild):
        warn("rebuild_phpconf not found.")
        return

    out, _, _ = run([rebuild, '--current'])
    lines = out.strip().splitlines()
    for i, line in enumerate(lines):
        line = line.strip()
        if i == 0:
            print(f"        {line}")
            continue
        m = re.search(r'ea-php(\d+)', line)
        if m:
            raw_ver = m.group(1)
            ver = f"{raw_ver[0]}.{raw_ver[1]}"
            eol = php_eol(ver)
            colored = date_check(eol)
            print(f"        {line}  {colored}")
        else:
            print(f"        {line}")


def _get_ea3_php():
    out, _, _ = run(['php', '-v'])
    m = re.search(r'PHP (\d+\.\d+)', out)
    if m:
        ver = m.group(1)
        eol = php_eol(ver)
        colored = date_check(eol)
        print(f"PHP {ver}  {colored}")


def _check_database():
    if not require_bin('mysql', 'database version check'):
        return

    out, _, _ = run(['mysql', '--version'])
    m = re.search(r'(?:Distrib |from )(\d+\.\d+\.\d+)(-MariaDB)?', out)
    if not m:
        bad(f"Could not parse DB version: {out.strip()}")
        return

    full_ver = m.group(1)
    is_mariadb = bool(m.group(2))
    engine = "MariaDB" if is_mariadb else "MySQL"
    major_minor = '.'.join(full_ver.split('.')[:2])

    if engine == "MySQL":
        good_versions = {'5.6', '5.7'}
        bad_versions = {'5.0', '5.1', '5.5'}
        if major_minor in good_versions or major_minor.startswith('8.'):
            good(f"MySQL {full_ver}")
        elif major_minor in bad_versions:
            bad(f"MySQL {full_ver}")
        else:
            bad(f"Unknown MySQL version: {full_ver}")
    else:
        good_minors = {'10.3', '10.4', '10.5', '10.6', '10.11'}
        bad_minors = {'10.0', '10.1', '10.2'}
        if major_minor in good_minors or major_minor.startswith('11.'):
            good(f"MariaDB {full_ver}")
        elif major_minor in bad_minors:
            bad(f"MariaDB {full_ver}")
        else:
            bad(f"Unknown MariaDB version: {full_ver}")


def _check_backups():
    config = '/var/cpanel/backups/config'
    if not os.path.isfile(config):
        warn("Backup config not found.")
        return
    with open(config, 'r') as f:
        content = f.read()
    m = re.search(r'BACKUPENABLE:\s*["\']?(\w+)["\']?', content)
    if m and m.group(1).lower() == 'no':
        bad2("Backups are not enabled.")
    else:
        good2("Backups are enabled.")


def _check_services(os_ver):
    """Check service status via whmapi1 servicestatus."""
    import json as _json
    out, _, rc = run(['whmapi1', '--output=json', 'servicestatus'])
    if rc != 0 or not out.strip():
        # fallback to systemd
        if os_ver and os_ver >= 7:
            _check_service_systemd('httpd', 'Apache (httpd)')
            _check_mysql_service()
            _check_dns_service_systemd()
        return
    try:
        data = _json.loads(out)
        services = data.get('data', {}).get('service', [])
    except (_json.JSONDecodeError, AttributeError):
        return

    for svc in services:
        if not svc.get('enabled'):
            continue
        if 'running' not in svc:
            continue
        name = svc.get('display_name', svc.get('name', '?'))
        if svc['running']:
            svc_ok(name)
        else:
            svc_stopped(name)


def _check_mysql_service():
    """Check MySQL/MariaDB service status and show mysqladmin version info."""
    # MariaDB on EL8/9 uses 'mariadb' unit; older systems use 'mysqld'
    for svc in ('mariadb', 'mysqld'):
        out, _, _ = run(['systemctl', 'is-active', f'{svc}.service'])
        state = out.strip()
        if state == 'active':
            label = 'MariaDB' if svc == 'mariadb' else 'MySQL'
            # Get uptime from systemctl
            uptime_out, _, _ = run(['systemctl', 'status', f'{svc}.service'])
            uptime = ''
            for line in uptime_out.splitlines():
                if 'Active:' in line and ';' in line:
                    uptime = line.split(';')[-1].strip().rstrip(')')
                    break
            good(f"{label} ({svc}) is up — {uptime}" if uptime else f"{label} ({svc}) is up.")
            # Show mysqladmin version detail
            adm_out, _, adm_rc = run(['mysqladmin', 'version'])
            if adm_rc == 0:
                for line in adm_out.splitlines():
                    line = line.strip()
                    if line and not line.startswith('Copyright') and not line.startswith('mysqladmin'):
                        print(f"        {line}")
            return
    bad("MySQL/MariaDB is down.")


def _check_service_systemd(service, label):
    out, _, _ = run(['service', service, 'status'])
    is_inactive = 'inactive' in out or 'failed' in out
    if is_inactive:
        bad(f"{label} is down.")
    else:
        # Extract uptime from systemctl
        uptime_out, _, _ = run(['systemctl', 'status', f'{service}.service'])
        uptime = ''
        for line in uptime_out.splitlines():
            if 'Active:' in line and ';' in line:
                uptime = line.split(';')[-1].strip().rstrip(')')
                break
        good(f"{label} is up — {uptime}" if uptime else f"{label} is up.")


def _check_dns_service_systemd():
    config = '/var/cpanel/cpanel.config'
    dns_type = 'bind'
    if os.path.isfile(config):
        with open(config, 'r') as f:
            for line in f:
                if 'local_nameserver_type' in line:
                    dns_type = line.split('=')[-1].strip()
                    break

    if dns_type == 'powerdns':
        _check_service_systemd('pdns', 'POWERDNS (pdns)')
    else:
        _check_service_systemd('named', 'NAMED (named)')


def _check_server_health(os_ver):
    # Disk
    out, _, _ = run(['df', '-h'])
    disk_pct = 0
    disk_total = '?'
    for line in out.splitlines()[1:]:
        parts = line.split()
        if len(parts) >= 6 and parts[5] == '/':
            disk_total = parts[1]
            disk_pct = int(parts[4].rstrip('%'))
            break

    disk_str = f"{disk_pct}% of {disk_total}"
    if disk_pct <= 49:
        disk_fmt = _inline_good2(disk_str)
    elif disk_pct <= 69:
        disk_fmt = _inline_good(disk_str)
    elif disk_pct <= 89:
        disk_fmt = _inline_bad(disk_str)
    else:
        disk_fmt = _inline_bad2(disk_str)

    # Memory
    mem_pct, mem_total_str = _get_memory(os_ver)
    mem_str = f"{mem_pct}% of {mem_total_str}"
    if mem_pct <= 49:
        mem_fmt = _inline_good2(mem_str)
    elif mem_pct <= 69:
        mem_fmt = _inline_good(mem_str)
    elif mem_pct <= 89:
        mem_fmt = _inline_bad(mem_str)
    else:
        mem_fmt = _inline_bad2(mem_str)

    # Load
    out, _, _ = run(['uptime'])
    load = ''
    m = re.search(r'average[s]?:\s*([\d., ]+)', out)
    if m:
        load = m.group(1).strip()
    try:
        load1 = float(load.split(',')[0].strip())
    except (ValueError, IndexError):
        load1 = 0.0

    if load1 <= 1.9:
        load_fmt = _inline_good2(load)
    elif load1 <= 3.9:
        load_fmt = _inline_bad(load)
    else:
        load_fmt = _inline_bad2(load)

    print(f"{disk_fmt}\t\t{mem_fmt}\t\t{load_fmt}")


def _get_memory(os_ver):
    out, _, _ = run(['vmstat', '-s'])
    lines = [l for l in out.splitlines()
             if not any(x in l for x in ('swap', 'cpu', 'pages', 'boot', 'forks', 'interrupt', 'switches'))]
    values = []
    for line in lines:
        m = re.match(r'\s*(\d+)', line)
        if m:
            values.append(int(m.group(1)))

    if not values:
        return 0, '?MB'

    # vmstat -s: index 0 = total memory, index 1 = used memory
    total_kb = values[0]
    used_kb = values[1] if len(values) > 1 else 0

    total_mb = total_kb / 1024
    used_mb = used_kb / 1024
    pct = int((used_mb / total_mb) * 100) if total_mb else 0
    return pct, f"{total_mb:.0f}MB"


def _check_exim_queue():
    if not require_bin('exim', 'Exim queue check'):
        return
    out, _, _ = run(['exim', '-bpc'])
    try:
        count = int(out.strip())
    except ValueError:
        warn(f"Could not parse Exim queue count: {out.strip()}")
        return
    if count > 100:
        bad2(f"Messages in queue: {count}")
    else:
        good(f"Messages in queue: {count}")




# ── Inline color helpers (return strings rather than printing) ─────────────────

def _inline_color(msg, fn):
    """Capture the color format without printing."""
    from modules.output import COLORAMA_AVAILABLE
    if not COLORAMA_AVAILABLE:
        return msg
    from colorama import Fore, Back, Style
    if fn == 'good2':
        return Back.GREEN + Fore.BLACK + f" {msg} " + Style.RESET_ALL
    elif fn == 'good':
        return Fore.GREEN + f"\u2714 {msg}" + Style.RESET_ALL
    elif fn == 'bad':
        return Fore.RED + f"X {msg}" + Style.RESET_ALL
    elif fn == 'bad2':
        return Back.RED + Fore.WHITE + f" {msg} " + Style.RESET_ALL
    return msg


def _inline_good2(msg): return _inline_color(msg, 'good2')
def _inline_good(msg):  return _inline_color(msg, 'good')
def _inline_bad(msg):   return _inline_color(msg, 'bad')
def _inline_bad2(msg):  return _inline_color(msg, 'bad2')
