"""
wordpress.py — WordPress management and audit functions.
"""

import os
import re
import subprocess
import urllib.request
import urllib.error
import json
import secrets
import string
from datetime import datetime, date, timedelta

from modules.output import warn, info, good, bad, magenta, header
from modules.utils import require_bin, run, get_dir_owner, generate_random_string


def wpfunctions():
    """Print the WordPress function help menu."""
    cmds = [
        ("wpaudit",       "Audit a WP install: siteurl, version, plugin/theme summary."),
        ("wpthemelist",   "List WP themes as the directory owner."),
        ("wpoldplugins",  "Check all plugins: version, last updated, flag old/outdated."),
        ("wppluginlist",  "List WP plugins as the directory owner."),
        ("wploginurl",    "Generate a one-time auto-login URL for WordPress."),
        ("wpsecurity",    "Find Wordfence and WP-Cerber installations under /home/."),
    ]
    print()
    header("WordPress Commands")
    print("-" * 70)
    for cmd, desc in cmds:
        print(f"  {cmd:<16} {desc}")
    print()


def wpoldplugins(cwd=None, inp=None):
    """
    Check all installed WP plugins against the WordPress.org API.
    Shows installed version, latest version, last updated date.
    Flags plugins not updated in 18+ months as [OLD PLUGIN].
    Flags outdated installs as [UPDATE AVAILABLE].
    """
    cwd, owner = _resolve_wp_cwd(cwd, inp=inp)
    if not cwd or not owner:
        return

    # Get installed plugins as JSON
    out, _, rc = run(['su', '-s', '/bin/bash', '-c',
                      f'wp --path={cwd} plugin list --format=json --fields=name,version,status',
                      owner])
    if rc != 0 or not out.strip():
        warn("Could not list plugins. Is this a WordPress directory?")
        return

    try:
        plugins = json.loads(out.strip())
    except (json.JSONDecodeError, ValueError):
        warn("Could not parse plugin list output.")
        return

    _SKIP = {'advanced-cache', 'maintenance'}
    plugins = [p for p in plugins if p.get('name', '').rstrip('.php') not in _SKIP]

    if not plugins:
        info("No plugins found.")
        return

    cutoff = date.today() - timedelta(days=548)  # ~18 months

    _WP_ORG_API = (
        'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information'
        '&request[slug]={slug}'
        '&request[fields][description]=0&request[fields][sections]=0'
        '&request[fields][tested]=0&request[fields][requires]=0'
        '&request[fields][rating]=0&request[fields][ratings]=0'
        '&request[fields][downloaded]=0&request[fields][downloadlink]=0'
        '&request[fields][versions]=0&request[fields][contributors]=0'
        '&request[fields][screenshots]=0&request[fields][banners]=0'
        '&request[fields][tags]=0&request[fields][author]=0'
        '&request[fields][author_profile]=0&request[fields][requires_php]=0'
    )

    rows = []
    for p in plugins:
        slug = p.get('name', '')
        installed = p.get('version', '?')
        display_name = slug
        latest = 'N/A'
        updated_str = 'N/A'
        old_flag = ''
        status = ''

        try:
            url = _WP_ORG_API.format(slug=slug)
            req = urllib.request.Request(url, headers={'User-Agent': 'vps-tool/2.0'})
            with urllib.request.urlopen(req, timeout=8) as resp:
                data = json.loads(resp.read().decode('utf-8'))

            if isinstance(data, dict) and 'error' not in data:
                display_name = re.sub(r'<[^>]+>', '', data.get('name', slug)).strip()
                latest = data.get('version', 'N/A')
                raw_date = data.get('last_updated', '')
                if raw_date:
                    try:
                        dt = datetime.strptime(raw_date, '%Y-%m-%d %I:%M%p %Z')
                        updated_str = dt.strftime('%-m/%-d/%Y')
                        if dt.date() < cutoff:
                            old_flag = '[OLD PLUGIN]'
                    except ValueError:
                        updated_str = raw_date[:10]
                if latest == installed:
                    status = '[OK - OLD]' if old_flag else '[OK]'
                else:
                    status = '[UPDATE AVAILABLE]'
            else:
                status = '[NOT ON WP.ORG]'
        except Exception:
            status = '[NOT ON WP.ORG]'

        rows.append((display_name, installed, latest, updated_str, old_flag, status))

    name_w = max(20, max(len(r[0]) for r in rows) + 1)
    print()
    print(f"  {'Plugin':<{name_w}}  {'Installed':<12}  {'Latest':<12}  {'Updated':<12}  {'OLD':<13}  Status")
    print("  " + "-" * (name_w + 60))
    for display_name, installed, latest, updated_str, old_flag, status in rows:
        print(f"  {display_name:<{name_w}}  {installed:<12}  {latest:<12}  {updated_str:<12}  {old_flag:<13}  {status}")
    print()


def wppluginlist(cwd=None, inp=None):
    """Run wp plugin list as the directory owner."""
    cwd, _ = _resolve_wp_cwd(cwd, inp=inp)
    if cwd:
        _wp_run_as_owner('wp plugin list', cwd)


def wpthemelist(cwd=None, inp=None):
    """Run wp theme list as the directory owner."""
    cwd, _ = _resolve_wp_cwd(cwd, inp=inp)
    if cwd:
        _wp_run_as_owner('wp theme list', cwd)


def wploginurl(cwd=None, inp=None):
    """
    Generate a one-time WordPress auto-login URL.
    Drops a self-deleting PHP script into the WP docroot.
    Bypasses plugins (Wordfence etc.) by defining a fake WP_PLUGIN_DIR.
    """
    cwd, owner = _resolve_wp_cwd(cwd, inp=inp)
    if not cwd or not owner:
        return

    # Get siteurl via wp-cli
    out, _, rc = run(['su', '-s', '/bin/bash', '-c',
                      f'wp --path={cwd} option get siteurl', owner])
    if rc != 0:
        warn("Could not get siteurl. Is this a WordPress directory?")
        return
    wp_url = out.strip().rstrip('/')

    # Generate a random access ID
    access_id = generate_random_string(12)

    # Load the autologin PHP template
    template_path = os.path.join(
        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
        'src', 'wp-autologin.php'
    )
    if not os.path.isfile(template_path):
        warn(f"Template not found: {template_path}")
        return

    with open(template_path, 'r') as f:
        php_content = f.read()

    # Replace Softaculous placeholders with safe values
    php_content = php_content.replace("'1234'", f"'{access_id}'")
    php_content = php_content.replace(
        "'[[softpath]]/[[autopass]]'", "'/tmp/noplugins'"
    )

    # Write to docroot
    access_path = os.path.join(cwd, 'access.php')
    try:
        with open(access_path, 'w') as f:
            f.write(php_content)
        run(['chown', f'{owner}:{owner}', access_path])
        run(['chmod', '644', access_path])
    except Exception as e:
        warn(f"Could not write access.php: {e}")
        return

    print(f"\nLogin URL: {wp_url}/access.php?id={access_id}")
    info("File will self-delete on first access.")


def wpaudit(cwd=None, inp=None):
    """
    Quick WordPress installation audit.
    Displays site URL, file path, core version, and plugin/theme counts.
    """
    if not require_bin('wp', 'wpaudit'):
        return

    cwd, owner = _resolve_wp_cwd(cwd, inp=inp)
    if not cwd or not owner:
        return

    def wp_run(subcmd):
        out, _, _ = run(['su', '-s', '/bin/bash', '-c',
                         f'wp --path={cwd} {subcmd}', owner])
        return out.strip()

    siteurl = wp_run('wp option get siteurl')
    version = wp_run('wp core version')
    plugins_raw = wp_run('wp plugin list')
    themes_raw = wp_run('wp theme list')

    def count_lines(raw, keyword=None):
        lines = raw.strip().splitlines()[1:]  # skip header row
        if keyword:
            return sum(1 for l in lines if keyword in l)
        return len(lines)

    print(f"WP siteurl:  {siteurl}")
    print(f"WP filepath: {cwd}")
    print(f"WP version:  {version}")
    print(f"WP themes:   {count_lines(themes_raw)} total, "
          f"{count_lines(themes_raw, 'available')} updates, "
          f"{count_lines(themes_raw, 'inactive')} inactive")
    print(f"WP plugins:  {count_lines(plugins_raw)} total, "
          f"{count_lines(plugins_raw, 'available')} updates, "
          f"{count_lines(plugins_raw, 'inactive')} inactive")


def wpsecurity():
    """Find WordPress security plugin installations across /home/."""
    from modules.security import wpsecurity as _ws
    _ws()


def _resolve_wp_cwd(cwd=None, inp=None):
    """
    Resolve a WordPress directory path and its owner.
    If cwd has wp-config.php, use it directly.
    Otherwise use inp (if supplied) or prompt for domain/path.
    Returns (cwd, owner) or (None, None) on failure.
    """
    if cwd is None:
        cwd = os.getcwd()

    if os.path.isfile(os.path.join(cwd, 'wp-config.php')):
        return cwd, get_dir_owner(cwd)

    # Use pre-supplied input (e.g. from command line) or prompt
    if inp is None:
        try:
            inp = input("  Domain or path (e.g. example.com, example.com/wp, /home/user/public_html): ").strip()
        except (EOFError, KeyboardInterrupt):
            return None, None
    if not inp:
        warn("No input provided.")
        return None, None

    # Absolute path
    if inp.startswith('/'):
        if not os.path.isfile(os.path.join(inp, 'wp-config.php')):
            warn(f"No wp-config.php found in: {inp}")
            return None, None
        return inp, get_dir_owner(inp)

    # Domain (with optional subpath)
    parts = inp.split('/', 1)
    base_domain = parts[0]
    subpath = parts[1].strip('/') if len(parts) > 1 else ''

    out, _, rc = run(['whmapi1', '--output=json', 'domainuserdata', f'domain={base_domain}'])
    if rc != 0:
        warn(f"Could not look up domain: {base_domain}")
        return None, None
    try:
        data = json.loads(out)
        if data.get('metadata', {}).get('result') != 1:
            warn(f"Domain not found on this server: {base_domain}")
            return None, None
        docroot = data['data']['userdata']['documentroot']
        owner = data['data']['userdata']['user']
    except (json.JSONDecodeError, KeyError):
        warn("Could not parse whmapi1 domainuserdata output.")
        return None, None

    resolved = os.path.join(docroot, subpath) if subpath else docroot
    if not os.path.isfile(os.path.join(resolved, 'wp-config.php')):
        warn(f"No wp-config.php found in: {resolved}")
        return None, None

    return resolved, owner


def _wp_run_as_owner(subcmd, cwd=None):
    """Run a wp-cli command as the directory owner (cwd must already be resolved)."""
    if not require_bin('wp', subcmd.split()[0]):
        return

    owner = get_dir_owner(cwd)
    if not owner:
        warn("Could not determine directory owner.")
        return

    # Convert 'wp subcommand ...' to include --path=
    wp_args = subcmd if subcmd.startswith('wp ') else f'wp {subcmd}'
    if '--path=' not in wp_args:
        wp_args = wp_args.replace('wp ', f'wp --path={cwd} ', 1)

    rc = subprocess.run(
        ['su', '-s', '/bin/bash', '-c', wp_args, owner]
    ).returncode
    if rc != 0:
        warn(f"Command exited with code {rc}. Is this a WordPress directory?")
