"""
dns.py — DNS, domain, and SSL functions.
"""

import json
import os
import re
import socket
import subprocess
from datetime import datetime

from modules.output import warn, info, good, bad, header
from modules.utils import require_bin, require_file, run, term_width


def zone(domain):
    """Display non-comment entries from a domain's BIND zone file."""
    zone_file = f'/var/named/{domain}.db'
    if not require_file(zone_file, f'zone {domain}'):
        return

    with open(zone_file, 'r', errors='replace') as f:
        for line in f:
            # Skip lines starting with ; (comments) or blank
            stripped = line.lstrip()
            if stripped.startswith(';') or not stripped.strip():
                continue
            print(line.rstrip())


def checkssl(domain):
    """
    Check locally installed SSL certificate for a domain via cPanel API.
    Uses whmapi1 fetch_ssl_vhosts — no external connections.
    """
    out, _, rc = run(['whmapi1', '--output=json', 'fetch_ssl_vhosts'])
    if rc != 0:
        warn("Could not query cPanel SSL vhosts.")
        return

    try:
        data = json.loads(out)
        vhosts = data['data']['vhosts']
    except (json.JSONDecodeError, KeyError):
        warn("Could not parse SSL vhosts response.")
        return

    # Find the vhost matching this domain
    crt = None
    for vhost in vhosts:
        if vhost.get('servername') == domain:
            crt = vhost.get('crt')
            break

    if not crt:
        warn(f"No installed SSL certificate found for: {domain}")
        return

    domains = crt.get('domains', [])
    issuer = (crt.get('issuer.organizationName')
              or crt.get('issuer.commonName')
              or 'N/A')
    not_after_ts = crt.get('not_after')
    if not_after_ts:
        try:
            expires = datetime.fromtimestamp(int(not_after_ts)).strftime('%Y-%m-%d %H:%M:%S')
        except (ValueError, OSError):
            expires = str(not_after_ts)
    else:
        expires = 'N/A'

    self_signed = crt.get('is_self_signed', False)
    label = f"{issuer}{' [self-signed]' if self_signed else ''}"

    print(f"Domain:   {domain}")
    print(f"SANs:     {', '.join(domains) if domains else 'N/A'}")
    print(f"Issuer:   {label}")
    print(f"Expires:  {expires}")


def docroot(domain):
    """Return the document root for a given domain."""
    userdatadomains = '/etc/userdatadomains'
    if not require_file(userdatadomains, 'docroot'):
        return

    with open(userdatadomains, 'r', errors='replace') as f:
        for line in f:
            if line.startswith(f'{domain}:'):
                parts = line.split('==')
                if len(parts) >= 5:
                    print(parts[4].strip())
                    return

    warn(f"Domain not found: {domain}")


def lsdomains():
    """List all addon and main domains on the VPS, sorted by owner then domain."""
    userdatadomains = '/etc/userdatadomains'
    if not require_file(userdatadomains, 'lsdomains'):
        return

    # Parse owner directly from /etc/userdatadomains
    # Line format: [hostname ]domain: ==type==ip==ip==user==...
    # When multiple words before ==, the last word (before ': ') is the actual domain
    entries = []
    with open(userdatadomains, 'r', errors='replace') as f:
        for line in f:
            if '==addon==' not in line and '==main==' not in line:
                continue
            parts = line.split('==')
            if len(parts) < 4:
                continue
            # Take last space-separated word before the '==' as the domain
            raw_key = parts[0].rstrip(': ').strip()
            domain = raw_key.split()[-1] if raw_key else ''
            owner = parts[3].strip()
            if domain and owner:
                entries.append((domain, owner))

    entries.sort()  # sorts by (owner, domain)

    dom_w = max(45, max((len(d) for _, d in entries), default=45))
    header(f"{'Domain':<{dom_w}}  {'Owner'}")
    print("-" * (dom_w + 22))
    for owner, domain in entries:
        print(f"{domain:<{dom_w}}  {owner}")
