<?php
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Build the HTML email body from update log entries.
 *
 * Header order (top to bottom):
 *  1. Site Name + Site URL  (with external-link icon)
 *  2. Divider line
 *  3. Company logo + Company name side-by-side
 *  4. "This update was completed by [Admin] from [Logo] [Company]"
 *
 * @param array $log_entries  Row objects from wpmm_update_log.
 * @param int   $admin_id     Optional override for the performing administrator.
 */
function wpmm_build_email_body( $log_entries, $admin_id = 0, $manual_entries = [], $update_note = '', $is_resend = false, $original_sent_at = '' ) {

    $site_name = get_bloginfo( 'name' );
    $site_url  = get_bloginfo( 'url' );

    // Settings
    $s        = wpmm_get_settings();
    $logo_url = ! empty( $s['logo_url'] )     ? $s['logo_url']     : '';
    $company  = ! empty( $s['company_name'] ) ? $s['company_name'] : '';

    // Performing administrator
    $perf_admin = null;
    if ( $admin_id ) {
        $u = get_user_by( 'id', $admin_id );
        if ( $u ) { $perf_admin = $u; }
    }
    if ( ! $perf_admin ) {
        $perf_admin = wpmm_get_default_admin();
    }

    // Build full name: First Last preferred; fall back to display_name.
    $admin_display_name = '';
    if ( $perf_admin ) {
        $first = get_user_meta( $perf_admin->ID, 'first_name', true );
        $last  = get_user_meta( $perf_admin->ID, 'last_name',  true );
        $full  = trim( $first . ' ' . $last );
        $admin_display_name = $full ?: $perf_admin->display_name;
    }

    // ── Group entries by session so multiple update runs appear as separate
    //    date-labelled sections rather than one undifferentiated block.
    $sessions_ordered = []; // [ session_key => [ 'date' => ..., 'entries' => [...] ] ]

    if ( is_array( $log_entries ) ) {
        foreach ( $log_entries as $entry ) {
            $session_key = ( ! empty( $entry->session_id ) )
                ? $entry->session_id
                : 'legacy-' . substr( $entry->updated_at ?? '', 0, 10 );

            if ( ! isset( $sessions_ordered[ $session_key ] ) ) {
                $sessions_ordered[ $session_key ] = [
                    'date'    => $entry->updated_at ?? '',
                    'entries' => [],
                ];
            }
            $sessions_ordered[ $session_key ]['entries'][] = $entry;
        }
    }

    // ── De-duplicate within each session: most recent row per slug wins ───────
    // Rule: if a plugin was retried and the retry succeeded, suppress the
    // earlier failed row — only the successful outcome is shown.
    // The failed row remains in the database for audit purposes; this is
    // purely a presentational de-duplication at email-build time.
    foreach ( $sessions_ordered as $key => &$session ) {
        $seen = []; // slug => index of most recent row in entries array
        foreach ( $session['entries'] as $idx => $entry ) {
            $slug = $entry->item_slug ?? ( $entry->item_name ?? '' );
            if ( isset( $seen[ $slug ] ) ) {
                $prev_idx    = $seen[ $slug ];
                $prev_status = $session['entries'][ $prev_idx ]->status ?? '';
                $curr_status = $entry->status ?? '';
                // If the current row is a success and the previous was a failure,
                // remove the failed row — keep the success.
                if ( $curr_status === 'success' && $prev_status !== 'success' ) {
                    unset( $session['entries'][ $prev_idx ] );
                    $seen[ $slug ] = $idx;
                } elseif ( $curr_status !== 'success' && $prev_status === 'success' ) {
                    // Previous was success, current is failure — keep success, discard current.
                    unset( $session['entries'][ $idx ] );
                } else {
                    // Both same status — keep most recent (current).
                    unset( $session['entries'][ $prev_idx ] );
                    $seen[ $slug ] = $idx;
                }
            } else {
                $seen[ $slug ] = $idx;
            }
        }
        // Re-index after potential unsets.
        $session['entries'] = array_values( $session['entries'] );
    }
    unset( $session ); // break reference

    // Determine whether to show session date headers — only when more than
    // one distinct session is present in this email.
    $multi_session = count( $sessions_ordered ) > 1;

    // ── Row builder ───────────────────────────────────────────────────────────
    $build_rows = function ( array $entries ) {
        $html = '';
        foreach ( $entries as $entry ) {
            $success = isset( $entry->status ) && $entry->status === 'success';
            $icon    = $success ? '&#9989;' : '&#9888;';

            // Status badge — success stays green; failures use amber to avoid
            // alarming clients with large red text for routine license notices.
            $badge = $success
                ? '<span style="color:#16a34a;font-weight:700;">Updated Successfully</span>'
                : '<span style="color:#92400e;font-weight:700;">Needs Attention</span>';

            $old = isset( $entry->old_version ) ? esc_html( $entry->old_version ) : '';
            $new = isset( $entry->new_version ) && $entry->new_version
                ? ' &rarr; <strong>' . esc_html( $entry->new_version ) . '</strong>'
                : '';
            $ver = $old . $new;

            $note = '';
            if ( ! $success ) {
                $error_code = $entry->error_code ?? '';
                if ( $error_code === 'no_package' || $error_code === 'wpmm_requires_manual' ) {
                    // License-gated plugins — friendly client-facing message.
                    $note = '<br><small style="color:#92400e;">'
                          . 'This plugin requires a valid license key to download updates. '
                          . 'Please update it manually via Dashboard &rarr; Updates or the plugin\'s own settings page.'
                          . '</small>';
                } elseif ( $error_code === 'copy_failed' ) {
                    $note = '<br><small style="color:#92400e;">'
                          . 'The update files could not be written to disk. '
                          . 'Please check server file permissions and try again.'
                          . '</small>';
                } elseif ( $error_code ) {
                    $explain = wpmm_explain_error( $error_code );
                    $note    = '<br><small style="color:#92400e;">'
                             . esc_html( $explain['label'] ) . ': '
                             . esc_html( $explain['detail'] ) . '</small>';
                } elseif ( ! empty( $entry->message ) ) {
                    // Strip any DEBUG: diagnostics from the message before emailing.
                    $clean_msg = preg_replace( '/\s*DEBUG:.*$/s', '', $entry->message );
                    $note      = '<br><small style="color:#92400e;">' . esc_html( $clean_msg ) . '</small>';
                }
            }

            $name = isset( $entry->item_name ) ? esc_html( $entry->item_name ) : '(unknown)';

            // If item_name is a file path (e.g. perfmatters/perfmatters.php),
            // extract a readable name from the slug instead.
            if ( str_contains( $name, '/' ) || str_contains( $name, '.php' ) ) {
                $raw  = $entry->item_slug ?? $entry->item_name ?? '';
                $raw  = preg_replace( '/\.php$/', '', basename( $raw ) );
                $name = esc_html( ucwords( str_replace( [ '-', '_' ], ' ', $raw ) ) );
            }

            // Always show a reason for failed items — never leave the icon alone.
            if ( ! $success && empty( trim( strip_tags( $note ) ) ) ) {
                $note = '<br><small style="color:#92400e;">'
                      . 'This item could not be updated automatically. '
                      . 'Please update it manually via Dashboard &rarr; Updates.'
                      . '</small>';
            }

            // Row background — amber tint for failures so they stand out gently.
            $row_bg = $success ? '' : 'background:#fffbeb;';

            $html .= "
            <tr style='{$row_bg}'>
              <td style='padding:10px 14px;border-bottom:1px solid #e5e7eb;'>
                {$icon} <strong>{$name}</strong>
                <br><small style='color:#9ca3af;'>{$ver}</small>{$note}
              </td>
              <td style='padding:10px 14px;border-bottom:1px solid #e5e7eb;text-align:right;white-space:nowrap;'>{$badge}</td>
            </tr>";
        }
        return $html;
    };

    // ── Section builder ───────────────────────────────────────────────────────
    $build_section = function ( $title, array $entries ) use ( $build_rows ) {
        if ( empty( $entries ) ) {
            return '';
        }
        $rows = $build_rows( $entries );
        return "
        <h3 style='color:#1e3a5f;font-size:15px;margin:28px 0 10px;border-bottom:2px solid #e5e7eb;padding-bottom:6px;'>{$title}</h3>
        <table width='100%' cellpadding='0' cellspacing='0' style='border-collapse:collapse;border:1px solid #e5e7eb;border-radius:6px;overflow:hidden;'>
          <thead>
            <tr style='background:#f3f4f6;'>
              <th style='padding:9px 14px;text-align:left;font-size:12px;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;'>Item</th>
              <th style='padding:9px 14px;text-align:right;font-size:12px;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;'>Status</th>
            </tr>
          </thead>
          <tbody>{$rows}</tbody>
        </table>";
    };

    // ── Assemble update sections — grouped by session with date headers ───────
    $sections = '';

    foreach ( $sessions_ordered as $session_key => $session_data ) {
        $session_entries = $session_data['entries'];
        $session_date    = $session_data['date'];

        // Bucket this session's entries by type.
        $core_rows   = [];
        $plugin_rows = [];
        $theme_rows  = [];

        foreach ( $session_entries as $entry ) {
            $type = isset( $entry->item_type ) ? strtolower( trim( $entry->item_type ) ) : '';
            if ( $type === 'core' ) {
                $core_rows[] = $entry;
            } elseif ( $type === 'theme' || $type === 'themes' ) {
                $theme_rows[] = $entry;
            } else {
                $plugin_rows[] = $entry;
            }
        }

        // When multiple sessions are present, show a date header between them
        // so the client can clearly see which updates happened on which date.
        if ( $multi_session && $session_date ) {
            $label = date_i18n( 'l, F j, Y \a\t g:i A', strtotime( $session_date ) );
            $sections .= "
        <div style='margin:32px 0 8px;padding:10px 16px;background:#f0f9ff;border-left:4px solid #2563eb;border-radius:0 6px 6px 0;'>
          <span style='font-size:13px;font-weight:700;color:#1e3a5f;'>&#128197; Update Session: {$label}</span>
        </div>";
        }

        $sections .= $build_section( '&#127758; WordPress Core', $core_rows );
        $sections .= $build_section( '&#128268; Plugins',        $plugin_rows );
        $sections .= $build_section( '&#127912; Themes',          $theme_rows );
    }

    if ( $sections === '' ) {
        $sections = "<p style='color:#6b7280;font-size:13px;margin:24px 0;'>No update entries were found for this session.</p>";
    }

    // ── External updates since last report ───────────────────────────────────
    // Updates run outside Greenskeeper (WP Updates screen, Avada dashboard, etc.)
    // ── Updates outside Greenskeeper — grouped by product family ─────────────
    $external_section = '';
    if ( function_exists( 'wpmm_get_external_updates_grouped' ) ) {
        // Find the last sent email timestamp to scope the window.
        global $wpdb;
        $last_sent = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            'SELECT sent_at FROM ' . esc_sql( $wpdb->prefix . 'wpmm_email_log' ) .
            " WHERE status = 'sent' ORDER BY sent_at DESC LIMIT 1"
        );

        $grouped = wpmm_get_external_updates_grouped( $last_sent ?: '' );

        if ( ! empty( $grouped ) ) {
            $families_html = '';

            foreach ( $grouped as $family => $rows ) {
                // Family sub-heading.
                $family_color = ( $family === 'Other External Updates' ) ? '#6b7280' : '#1e3a5f';
                $families_html .= "
        <div style='margin-bottom:20px;'>
          <h4 style='margin:0 0 8px;font-size:13px;font-weight:700;color:{$family_color};
                     text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid #e5e7eb;
                     padding-bottom:5px;'>" . esc_html( $family ) . "</h4>
          <table width='100%' cellpadding='0' cellspacing='0'
                 style='border-collapse:collapse;border:1px solid #e5e7eb;border-radius:4px;overflow:hidden;'>
            <thead>
              <tr style='background:#f8fafc;'>
                <th style='padding:7px 12px;text-align:left;font-size:11px;color:#6b7280;
                           text-transform:uppercase;letter-spacing:.05em;'>Plugin / Theme</th>
                <th style='padding:7px 12px;text-align:left;font-size:11px;color:#6b7280;
                           text-transform:uppercase;letter-spacing:.05em;'>Version</th>
                <th style='padding:7px 12px;text-align:right;font-size:11px;color:#6b7280;
                           text-transform:uppercase;letter-spacing:.05em;'>Status</th>
              </tr>
            </thead>
            <tbody>";

                foreach ( $rows as $er ) {
                    $ename       = esc_html( $er->item_name );
                    $eold        = esc_html( $er->old_version );
                    $enew        = esc_html( $er->new_version );
                    $ver_display = $eold ? "{$eold} &rarr; {$enew}" : $enew;

                    $families_html .= "
              <tr>
                <td style='padding:9px 12px;border-bottom:1px solid #e5e7eb;font-size:13px;'>
                  <strong>{$ename}</strong>
                </td>
                <td style='padding:9px 12px;border-bottom:1px solid #e5e7eb;font-size:12px;
                           color:#6b7280;'>{$ver_display}</td>
                <td style='padding:9px 12px;border-bottom:1px solid #e5e7eb;text-align:right;'>
                  <span style='color:#16a34a;font-weight:700;font-size:12px;'>&#10003; Updated</span>
                </td>
              </tr>";
                }

                $families_html .= "
            </tbody>
          </table>
        </div>";
            }

            $external_section = "
        <h3 style='color:#1e3a5f;font-size:15px;margin:28px 0 6px;border-bottom:2px solid #e5e7eb;
                   padding-bottom:6px;'>&#128260; Updates Outside Greenskeeper</h3>
        <p style='font-size:12px;color:#6b7280;margin:0 0 16px;'>
          The following updates were applied through an external tool, proprietary dashboard,
          or the WordPress Updates screen. Greenskeeper detected these automatically by
          comparing installed versions.
        </p>
        {$families_html}";
        }
    }

    // ── Spam activity since last report ──────────────────────────────────────
    // Find the most recent sent email timestamp and query spam blocked since then.
    $spam_section = '';
    if ( function_exists( 'wpmm_get_spam_since_last_report' ) ) {
        $spam_rows = wpmm_get_spam_since_last_report();
        if ( ! empty( $spam_rows ) ) {
            $rule_labels = [
                'honeypot'       => 'Honeypot',
                'too_fast'       => 'Submission Too Fast',
                'blocked_ip'     => 'Blocked IP',
                'keyword'        => 'Keyword Match',
                'too_many_links' => 'Too Many Links',
                'duplicate'      => 'Duplicate Comment',
                'akismet'        => 'Akismet',
            ];
            $spam_rows_html = '';
            foreach ( $spam_rows as $sr ) {
                $rule    = esc_html( $rule_labels[ $sr->rule ] ?? $sr->rule );
                $ip      = esc_html( $sr->author_ip );
                $preview = esc_html( mb_substr( wp_strip_all_tags( $sr->comment_content ?? '' ), 0, 60 ) );
                $when    = esc_html( date_i18n( 'M j g:i A', strtotime( $sr->blocked_at ) ) );
                $spam_rows_html .= "
                <tr>
                  <td style='padding:8px 12px;border-bottom:1px solid #e5e7eb;font-size:12px;color:#6b7280;white-space:nowrap;'>{$when}</td>
                  <td style='padding:8px 12px;border-bottom:1px solid #e5e7eb;'><span style='font-size:11px;font-weight:700;color:#dc2626;'>{$rule}</span></td>
                  <td style='padding:8px 12px;border-bottom:1px solid #e5e7eb;font-size:12px;font-family:monospace;color:#374151;'>{$ip}</td>
                  <td style='padding:8px 12px;border-bottom:1px solid #e5e7eb;font-size:12px;color:#6b7280;'>{$preview}</td>
                </tr>";
            }
            $spam_count   = count( $spam_rows );
            $spam_section = "
        <h3 style='color:#1e3a5f;font-size:15px;margin:28px 0 10px;border-bottom:2px solid #e5e7eb;padding-bottom:6px;'>&#128737; Spam Activity Since Last Report</h3>
        <p style='font-size:12px;color:#6b7280;margin:0 0 10px;'>{$spam_count} comment attempt" . ( $spam_count === 1 ? '' : 's' ) . " blocked by the spam filter.</p>
        <table width='100%' cellpadding='0' cellspacing='0' style='border-collapse:collapse;border:1px solid #e5e7eb;border-radius:6px;overflow:hidden;'>
          <thead>
            <tr style='background:#fef2f2;'>
              <th style='padding:8px 12px;text-align:left;font-size:11px;color:#991b1b;text-transform:uppercase;letter-spacing:.05em;'>When</th>
              <th style='padding:8px 12px;text-align:left;font-size:11px;color:#991b1b;text-transform:uppercase;letter-spacing:.05em;'>Rule</th>
              <th style='padding:8px 12px;text-align:left;font-size:11px;color:#991b1b;text-transform:uppercase;letter-spacing:.05em;'>IP Address</th>
              <th style='padding:8px 12px;text-align:left;font-size:11px;color:#991b1b;text-transform:uppercase;letter-spacing:.05em;'>Content Preview</th>
            </tr>
          </thead>
          <tbody>{$spam_rows_html}</tbody>
        </table>";
        }
    }

    // ── Additional Manual Updates section ─────────────────────────────────────
    if ( ! empty( $manual_entries ) ) {
        $manual_rows = '';
        foreach ( $manual_entries as $entry ) {
            if ( empty( $entry['name'] ) ) continue;
            $old = esc_html( $entry['old_version'] ?? '' );
            $new = esc_html( $entry['new_version'] ?? '' );
            $ver = $old . ( $new ? ' &rarr; <strong>' . $new . '</strong>' : '' );
            $manual_rows .= "
            <tr>
              <td style='padding:10px 14px;border-bottom:1px solid #e5e7eb;'>
                &#9989; <strong>" . esc_html( $entry['name'] ) . "</strong>
                <br><small style='color:#9ca3af;'>" . $ver . "</small>
              </td>
              <td style='padding:10px 14px;border-bottom:1px solid #e5e7eb;text-align:right;white-space:nowrap;'>
                <span style='color:#16a34a;font-weight:700;'>Updated Manually</span>
              </td>
            </tr>";
        }
        if ( $manual_rows ) {
            $manual_note = "<p style='color:#6b7280;font-size:12px;font-style:italic;margin:8px 0 0;'>Plugins or themes updated manually outside the control of Greenskeeper due to functional licensing issues that prevent this plugin from accessing the specific panels where these plugins are located in the plugin or theme admin.</p>";
            $sections .= "
        <h3 style='color:#1e3a5f;font-size:15px;margin:28px 0 10px;border-bottom:2px solid #e5e7eb;padding-bottom:6px;'>&#128295; Additional Manual Updates</h3>
        " . $manual_note . "
        <table width='100%' cellpadding='0' cellspacing='0' style='margin-top:10px;border-collapse:collapse;border:1px solid #e5e7eb;border-radius:6px;overflow:hidden;'>
          <thead>
            <tr style='background:#f3f4f6;'>
              <th style='padding:9px 14px;text-align:left;font-size:12px;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;'>Item</th>
              <th style='padding:9px 14px;text-align:right;font-size:12px;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;'>Status</th>
            </tr>
          </thead>
          <tbody>" . $manual_rows . "</tbody>
        </table>";
        }
    }

    // ── Header: Site block (top) ──────────────────────────────────────────────
    $ext_icon = "<svg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24'"
              . " fill='none' stroke='#93c5fd' stroke-width='2.5' stroke-linecap='round'"
              . " stroke-linejoin='round' style='display:inline-block;vertical-align:middle;margin-left:3px;'>"
              . "<path d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/>"
              . "<polyline points='15 3 21 3 21 9'/><line x1='10' y1='14' x2='21' y2='3'/>"
              . "</svg>";

    $site_block = "
        <p style='margin:0 0 3px;color:#bfdbfe;font-size:11px;text-transform:uppercase;letter-spacing:.08em;'>Site</p>
        <p style='margin:0 0 3px;color:#ffffff;font-size:20px;font-weight:700;line-height:1.2;'>"
              . esc_html( $site_name ) . "</p>
        <p style='margin:0;font-size:13px;'>
          <a href='" . esc_url( $site_url ) . "' style='color:#93c5fd;text-decoration:none;'>"
              . esc_html( $site_url ) . $ext_icon . "</a>
        </p>";

    // ── Header: Brand row — [Logo] [Company Name] inline ─────────────────────
    // Logo rendered as an inline <img> inside a table cell so it sits on the
    // same baseline as the company name text across all email clients.
    $logo_img_inline = '';
    if ( $logo_url ) {
        $logo_img_inline = "<img src='" . esc_url( $logo_url ) . "'"
            . " alt='" . esc_attr( $company ) . "'"
            . " style='max-height:24px;max-width:90px;display:block;"
            . "filter:brightness(0) invert(1);'>";
    }

    // Build the brand row as an HTML table for reliable inline layout in email.
    if ( $logo_img_inline || $company ) {
        $brand_cells = '';
        if ( $logo_img_inline ) {
            $brand_cells .= "<td style='vertical-align:middle;padding-right:10px;'>"
                          . $logo_img_inline . "</td>";
        }
        if ( $company ) {
            $brand_cells .= "<td style='vertical-align:middle;'>"
                          . "<span style='color:#fff;font-size:16px;font-weight:700;line-height:1;'>"
                          . esc_html( $company ) . "</span></td>";
        }
        $brand_block = "<table cellpadding='0' cellspacing='0' border='0'>"
                     . "<tr>" . $brand_cells . "</tr></table>";
    } else {
        $brand_block = '';
    }

    // ── Header: Administered-by line ─────────────────────────────────────────
    $administered_by = '';
    if ( $perf_admin ) {
        $admin_name      = esc_html( $admin_display_name );
        $administered_by = "<p style='color:#bfdbfe;font-size:13px;margin:8px 0 0;line-height:1.6;'>"
                         . "WordPress website updates administered by "
                         . "<strong style='color:#fff;'>" . $admin_display_name . "</strong>.</p>";
    }

    // ── Divider between site block and brand row ──────────────────────────────
    $divider = ( $brand_block || $administered_by )
        ? "<div style='border-top:1px solid rgba(255,255,255,.2);margin:16px 0 14px;'></div>"
        : '';

    // ── Footer ────────────────────────────────────────────────────────────────
    $sender = $company ?: 'Greenskeeper';

    // ── Update note block ─────────────────────────────────────────────────────
    $update_note_block = '';
    if ( ! empty( $update_note ) ) {
        $note_lines = nl2br( esc_html( $update_note ) );
        $update_note_block = '
    <div style="margin-top:28px;">
      <div style="background:#fffbeb;border:1px solid #fde68a;border-radius:8px;padding:18px 22px;">
        <p style="margin:0 0 8px;font-size:12px;font-weight:700;color:#92400e;text-transform:uppercase;letter-spacing:.06em;">Note from your administrator</p>
        <p style="margin:0;font-size:14px;color:#78350f;line-height:1.7;">' . $note_lines . '</p>
      </div>
    </div>';
    }

    // ── Resend banner ─────────────────────────────────────────────────────────
    // Shown at the top of the email body when a previously sent report is
    // being resent, so the recipient understands the context immediately.
    $resend_banner = '';
    if ( $is_resend && $original_sent_at ) {
        $orig_date      = date_i18n( 'F j, Y', strtotime( $original_sent_at ) );
        $orig_time      = date_i18n( 'g:i A', strtotime( $original_sent_at ) );
        $resend_banner  = '
    <div style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;
                padding:14px 18px;margin-bottom:24px;font-size:13px;color:#1e40af;line-height:1.6;">
      <strong>&#8505; Updated Report:</strong>
      This is an update to a maintenance report originally sent on
      <strong>' . esc_html( $orig_date ) . '</strong> at <strong>' . esc_html( $orig_time ) . '</strong>.
      The updates listed below were performed during that session.
    </div>';
    }

    // ── Assemble ─────────────────────────────────────────────────────────────
    return '<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>WordPress Maintenance Report</title>
</head>
<body style="font-family:Georgia,serif;background:#f1f5f9;margin:0;padding:0;">
  <div style="max-width:680px;margin:40px auto;background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 4px 16px rgba(0,0,0,.1);">

    <!-- HEADER -->
    <div style="background:#1e3a5f;padding:28px 36px 24px;">
      ' . $site_block . '
      ' . $divider . '
      ' . $brand_block . '
      ' . $administered_by . '
    </div>

    <!-- BODY — padding-bottom is intentionally generous so the last section
         never collides with the footer regardless of content length. -->
    <div style="padding:32px 36px 48px;">
      <h2 style="color:#1e3a5f;margin:0 0 6px;font-size:18px;font-family:Georgia,serif;">Weekly WordPress Maintenance Report</h2>
      <p style="color:#6b7280;font-size:13px;margin:0 0 4px;">The following updates were performed on your site.</p>
      ' . $resend_banner . '
      ' . $update_note_block . '
      ' . $sections . '
      ' . $external_section . '
      ' . $spam_section . '
    </div>

    <!-- FOOTER — separated from body by a full border-top; never overlaps content -->
    <div style="background:#f8fafc;padding:20px 36px;border-top:2px solid #e5e7eb;text-align:center;font-size:12px;color:#9ca3af;clear:both;">
      Sent by ' . esc_html( $sender ) . '
      &bull; <a href="' . esc_url( $site_url ) . '" style="color:#9ca3af;">' . esc_html( $site_url ) . '</a>
    </div>

  </div>
</body>
</html>';
}


/**
 * Return external update log entries (session_id starting with 'ext-')
 * that occurred since the last sent email report.
 * These are updates run outside Greenskeeper — via the WP Updates screen,
 * the Avada plugins dashboard, etc.
 */
function wpmm_get_external_updates_since_last_report() {
    global $wpdb;
    $log_table   = esc_sql( $wpdb->prefix . 'wpmm_update_log' );
    $email_table = esc_sql( $wpdb->prefix . 'wpmm_email_log' );

    // Find the most recent successfully sent email.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $last_sent = $wpdb->get_var(
        "SELECT sent_at FROM {$email_table} WHERE status = 'sent' ORDER BY sent_at DESC LIMIT 1" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    );

    if ( $last_sent ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
        return $wpdb->get_results( $wpdb->prepare(
            'SELECT * FROM ' . $log_table . " WHERE session_id LIKE %s AND updated_at > %s ORDER BY updated_at ASC",
            'ext-%',
            $last_sent
        ) );
    }

    // No previous report — show all external entries.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
    return $wpdb->get_results( $wpdb->prepare(
        'SELECT * FROM ' . $log_table . ' WHERE session_id LIKE %s ORDER BY updated_at ASC',
        'ext-%'
    ) );
}


/**
 * Return spam log entries blocked since the last sent email report.
 * Limited to 50 rows to keep the email a reasonable length.
 */
function wpmm_get_spam_since_last_report() {
    global $wpdb;
    $spam_table  = esc_sql( $wpdb->prefix . 'wpmm_spam_log' );
    $email_table = esc_sql( $wpdb->prefix . 'wpmm_email_log' );

    // Find the most recent successfully sent email timestamp.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $last_sent = $wpdb->get_var(
        "SELECT sent_at FROM {$email_table} WHERE status = 'sent' ORDER BY sent_at DESC LIMIT 1" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    );

    if ( $last_sent ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return $wpdb->get_results( $wpdb->prepare(
            "SELECT * FROM {$spam_table} WHERE blocked_at > %s ORDER BY blocked_at DESC LIMIT 50", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $last_sent
        ) );
    }

    // No previous report — show last 50 all-time.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    return $wpdb->get_results(
        "SELECT * FROM {$spam_table} ORDER BY blocked_at DESC LIMIT 50" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    );
}

/**
 * Send the maintenance email and log it to wpmm_email_log.
 *
 * @param string $to        Recipient email address.
 * @param string $subject   Email subject line.
 * @param string $body      Full HTML email body.
 * @param int    $admin_id  Performing administrator user ID.
 * @param string $note      Raw administrator note text (stored separately so
 *                          previews can always display it regardless of body version).
 */
function wpmm_send_email( $to, $subject, $body, $admin_id = 0, $note = '', $session_id = '' ) {
    global $wpdb;

    $from_name  = 'Greenskeeper';
    $from_email = get_option( 'admin_email' );

    $s = wpmm_get_settings();
    if ( ! empty( $s['company_name'] ) ) {
        $from_name = $s['company_name'];
    }

    $perf_id = $admin_id ?: ( $s['default_admin_id'] ?? 0 );
    if ( $perf_id ) {
        $u = get_user_by( 'id', $perf_id );
        if ( $u ) {
            $from_name  = $u->display_name;
            $from_email = $u->user_email;
        }
    }

    $headers = [
        'Content-Type: text/html; charset=UTF-8',
        'From: ' . $from_name . ' <' . $from_email . '>',
    ];

    // Use wpmm_wp_mail() so Greenskeeper's phpmailer_init hook fires
    // only for this email, never for other site mail.
    $sent   = wpmm_wp_mail( $to, $subject, $body, $headers );
    $status = $sent ? 'sent' : 'failed';

    // Use the passed session_id if provided, fall back to last_session.
    if ( ! $session_id ) {
        $last_session = get_option( 'wpmm_last_session', [] );
        $session_id   = $last_session['session_id'] ?? '';
    }

    $log_table = esc_sql( $wpdb->prefix . 'wpmm_email_log' );

    // Check whether the note column exists — it was added in v2.1.6.
    // If it doesn't exist yet (older install), insert without it.
    $columns = $wpdb->get_col( "DESCRIBE {$log_table}", 0 ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    $has_note = in_array( 'note', $columns, true );

    $row_data = [
        'session_id' => $session_id,
        'to_email'   => $to,
        'subject'    => $subject,
        'body'       => $body,
        'status'     => $status,
        'sent_at'    => current_time( 'mysql' ),
    ];
    if ( $has_note ) {
        $row_data['note'] = $note;
    }

    $result = $wpdb->insert( $log_table, $row_data ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery

    if ( $result === false && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
        trigger_error( 'Greenskeeper: email log insert failed: ' . esc_html( $wpdb->last_error ), E_USER_WARNING );
    }

    return [ 'success' => $sent, 'email_id' => (int) $wpdb->insert_id ];
}

/**
 * Build a consolidated network maintenance email.
 * Called when updates were run in All Sites mode.
 *
 * @param array $sites_data  Array of [ 'blog_id' => N, 'site_name' => '', 'site_url' => '', 'entries' => [...] ]
 * @param int   $admin_id    Performing administrator.
 * @param string $update_note Optional note from administrator.
 */
function wpmm_build_network_email_body( $sites_data, $admin_id = 0, $update_note = '' ) {

    $s        = wpmm_get_settings();
    $logo_url = ! empty( $s['logo_url'] )     ? $s['logo_url']     : '';
    $company  = ! empty( $s['company_name'] ) ? $s['company_name'] : '';

    $perf_admin = null;
    if ( $admin_id ) {
        $u = get_user_by( 'id', $admin_id );
        if ( $u ) { $perf_admin = $u; }
    }
    if ( ! $perf_admin ) {
        $perf_admin = wpmm_get_default_admin();
    }

    // ── Build per-site sections ───────────────────────────────────────────────
    $all_sections = '';
    foreach ( $sites_data as $site ) {
        $site_name = esc_html( $site['site_name'] ?? '' );
        $site_url  = esc_url( $site['site_url']  ?? '' );
        $entries   = $site['entries'] ?? [];

        if ( empty( $entries ) ) {
            continue;
        }

        $build_rows = function( array $ents ) {
            $html = '';
            foreach ( $ents as $entry ) {
                $success = isset( $entry->status ) && $entry->status === 'success';
                $icon    = $success ? '&#9989;' : '&#10060;';
                $badge   = $success
                    ? '<span style="color:#16a34a;font-weight:700;">Updated Successfully</span>'
                    : '<span style="color:#dc2626;font-weight:700;">Update Failed</span>';
                $old = isset( $entry->old_version ) ? esc_html( $entry->old_version ) : '';
                $new = isset( $entry->new_version ) && $entry->new_version
                    ? ' &rarr; <strong>' . esc_html( $entry->new_version ) . '</strong>' : '';
                $name = isset( $entry->item_name ) ? esc_html( $entry->item_name ) : '(unknown)';
                $html .= "<tr>
                  <td style='padding:10px 14px;border-bottom:1px solid #e5e7eb;'>
                    {$icon} <strong>{$name}</strong>
                    <br><small style='color:#9ca3af;'>{$old}{$new}</small>
                  </td>
                  <td style='padding:10px 14px;border-bottom:1px solid #e5e7eb;text-align:right;'>{$badge}</td>
                </tr>";
            }
            return $html;
        };

        $core_rows = $plugin_rows = $theme_rows = [];
        foreach ( $entries as $entry ) {
            $type = strtolower( trim( $entry->item_type ?? '' ) );
            if ( $type === 'core' )        { $core_rows[]   = $entry; }
            elseif ( $type === 'theme' )   { $theme_rows[]  = $entry; }
            else                           { $plugin_rows[] = $entry; }
        }

        $build_section = function( $title, $ents ) use ( $build_rows ) {
            if ( empty( $ents ) ) return '';
            $rows = $build_rows( $ents );
            return "<h4 style='color:#374151;font-size:13px;margin:16px 0 8px;'>{$title}</h4>
            <table width='100%' cellpadding='0' cellspacing='0' style='border-collapse:collapse;border:1px solid #e5e7eb;'>
              <thead><tr style='background:#f3f4f6;'>
                <th style='padding:8px 12px;text-align:left;font-size:11px;color:#6b7280;text-transform:uppercase;'>Item</th>
                <th style='padding:8px 12px;text-align:right;font-size:11px;color:#6b7280;text-transform:uppercase;'>Status</th>
              </tr></thead>
              <tbody>{$rows}</tbody>
            </table>";
        };

        $site_sections = $build_section( '&#127758; WordPress Core', $core_rows )
                       . $build_section( '&#128268; Plugins', $plugin_rows )
                       . $build_section( '&#127912; Themes', $theme_rows );

        $all_sections .= "
        <div style='margin:28px 0 8px;padding:14px 16px;background:#f0f7ff;border-left:4px solid #2563eb;border-radius:0 6px 6px 0;'>
            <p style='margin:0;font-size:15px;font-weight:700;color:#1e3a5f;'>{$site_name}</p>
            <p style='margin:2px 0 0;font-size:12px;color:#6b7280;'>
                <a href='{$site_url}' style='color:#2563eb;text-decoration:none;'>{$site_url}</a>
            </p>
        </div>
        {$site_sections}";
    }

    if ( $all_sections === '' ) {
        $all_sections = "<p style='color:#6b7280;font-size:13px;margin:24px 0;'>No updates were performed on any site in this network run.</p>";
    }

    // ── Header elements (same as single-site) ─────────────────────────────────
    $network_url  = network_site_url();
    $network_name = get_network()->site_name ?? 'WordPress Network';

    $ext_icon = "<svg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='#93c5fd' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' style='display:inline-block;vertical-align:middle;margin-left:3px;'><path d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/><polyline points='15 3 21 3 21 9'/><line x1='10' y1='14' x2='21' y2='3'/></svg>";

    $site_block = "<p style='margin:0 0 3px;color:#bfdbfe;font-size:11px;text-transform:uppercase;letter-spacing:.08em;'>Network</p>
        <p style='margin:0 0 3px;color:#ffffff;font-size:20px;font-weight:700;line-height:1.2;'>" . esc_html( $network_name ) . "</p>
        <p style='margin:0;font-size:13px;'><a href='" . esc_url( $network_url ) . "' style='color:#93c5fd;text-decoration:none;'>" . esc_html( $network_url ) . $ext_icon . "</a></p>";

    $logo_img = $logo_url ? "<img src='" . esc_url( $logo_url ) . "' alt='" . esc_attr( $company ) . "' style='max-height:24px;max-width:90px;display:block;filter:brightness(0) invert(1);'>" : '';
    $brand_cells = '';
    if ( $logo_img )  { $brand_cells .= "<td style='vertical-align:middle;padding-right:10px;'>{$logo_img}</td>"; }
    if ( $company )   { $brand_cells .= "<td style='vertical-align:middle;'><span style='color:#fff;font-size:16px;font-weight:700;'>" . esc_html( $company ) . "</span></td>"; }
    $brand_block = $brand_cells ? "<table cellpadding='0' cellspacing='0' border='0'><tr>{$brand_cells}</tr></table>" : '';

    $administered_by = $perf_admin
        ? "<p style='color:#bfdbfe;font-size:13px;margin:8px 0 0;'>Network maintenance performed by <strong style='color:#fff;'>" . esc_html( $perf_admin->display_name ) . "</strong>.</p>"
        : '';

    $divider = ( $brand_block || $administered_by ) ? "<div style='border-top:1px solid rgba(255,255,255,.2);margin:16px 0 14px;'></div>" : '';
    $sender  = $company ?: 'Greenskeeper';

    $update_note_block = '';
    if ( ! empty( $update_note ) ) {
        $note_lines = nl2br( esc_html( $update_note ) );
        $update_note_block = '<div style="padding:24px 36px 0;"><div style="background:#fffbeb;border:1px solid #fde68a;border-radius:8px;padding:18px 22px;"><p style="margin:0 0 8px;font-size:12px;font-weight:700;color:#92400e;text-transform:uppercase;letter-spacing:.06em;">Note from your administrator</p><p style="margin:0;font-size:14px;color:#78350f;line-height:1.7;">' . $note_lines . '</p></div></div>';
    }

    return '<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Network Maintenance Report</title></head>
<body style="font-family:Georgia,serif;background:#f1f5f9;margin:0;padding:0;">
  <div style="max-width:680px;margin:40px auto;background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 4px 16px rgba(0,0,0,.1);">
    <div style="background:#1e3a5f;padding:28px 36px 24px;">
      ' . $site_block . '
      ' . $divider . '
      ' . $brand_block . '
      ' . $administered_by . '
    </div>
    <div style="padding:32px 36px;">
      <h2 style="color:#1e3a5f;margin:0 0 6px;font-size:18px;font-family:Georgia,serif;">Network WordPress Maintenance Report</h2>
      <p style="color:#6b7280;font-size:13px;margin:0 0 4px;">The following updates were performed across your network.</p>
      ' . $all_sections . '
    </div>
    ' . $update_note_block . '
    <div style="background:#f8fafc;padding:18px 36px;border-top:1px solid #e5e7eb;text-align:center;font-size:12px;color:#9ca3af;">
      Sent by ' . esc_html( $sender ) . ' &bull; <a href="' . esc_url( $network_url ) . '" style="color:#9ca3af;">' . esc_html( $network_url ) . '</a>
    </div>
  </div>
</body></html>';
}

/**
 * Send an internal admin notification email after a batch completes.
 *
 * This is a separate internal email — NOT the client-facing report.
 * It goes to the performing administrator to inform them of the outcome.
 *
 * @param array  $results    Array of update result arrays from wpmm_do_update().
 * @param int    $admin_id   Performing administrator user ID.
 * @param string $session_id Session identifier for the batch.
 */
function wpmm_send_admin_notification( array $results, $admin_id = 0, $session_id = '' ) {
    if ( empty( $results ) ) {
        return;
    }

    $s = wpmm_get_settings();

    // Determine which notification types are enabled.
    $notify_all_success     = ! empty( $s['notify_all_success'] );
    $notify_partial_success = ! empty( $s['notify_partial_success'] );
    $notify_all_failed      = ! empty( $s['notify_all_failed'] );

    // Tally results.
    $successes = array_filter( $results, function( $r ) { return ( $r['status'] ?? '' ) === 'success'; } );
    $failures  = array_filter( $results, function( $r ) { return ( $r['status'] ?? '' ) !== 'success'; } );

    $all_succeeded  = count( $failures ) === 0;
    $all_failed     = count( $successes ) === 0;
    $partial        = ! $all_succeeded && ! $all_failed;

    // Check whether this notification type is enabled.
    if ( $all_succeeded && ! $notify_all_success )     { return; }
    if ( $partial       && ! $notify_partial_success ) { return; }
    if ( $all_failed    && ! $notify_all_failed )      { return; }

    // Resolve recipient — performing admin, then default admin, then admin_email.
    $recipient = null;
    if ( $admin_id ) {
        $recipient = get_user_by( 'id', $admin_id );
    }
    if ( ! $recipient ) {
        $recipient = wpmm_get_default_admin();
    }
    $to = $recipient ? $recipient->user_email : get_option( 'admin_email' );
    if ( ! $to ) {
        return;
    }

    $site_name = get_bloginfo( 'name' );
    $site_url  = get_bloginfo( 'url' );

    // Subject line reflects outcome.
    if ( $all_succeeded ) {
        $subject = '[Greenskeeper] ✓ All updates completed — ' . $site_name;
    } elseif ( $all_failed ) {
        $subject = '[Greenskeeper] ✗ All updates failed — ' . $site_name;
    } else {
        $subject = '[Greenskeeper] ⚠ ' . count( $failures ) . ' update(s) need attention — ' . $site_name;
    }

    // Build email body.
    $rows_success = '';
    foreach ( $successes as $r ) {
        $rows_success .= '<tr>'
            . '<td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;">'
            . esc_html( $r['name'] ?? $r['slug'] ?? '' )
            . '</td>'
            . '<td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;color:#6b7280;font-size:12px;">'
            . esc_html( $r['old_version'] ?? '' ) . ' &rarr; ' . esc_html( $r['new_version'] ?? '' )
            . '</td>'
            . '<td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;">'
            . '<span style="color:#16a34a;font-weight:700;">&#10003; Updated</span>'
            . '</td>'
            . '</tr>';
    }

    $rows_failed = '';
    foreach ( $failures as $r ) {
        $error_code = $r['error_code'] ?? '';
        $reason     = $error_code === 'no_package' || $error_code === 'wpmm_requires_manual'
            ? 'License required — update manually'
            : ( $r['message'] ?? '' );
        // Strip DEBUG strings.
        $reason = preg_replace( '/\s*DEBUG:.*$/s', '', trim( $reason ) );
        // Always show a reason — never leave the icon alone.
        if ( empty( $reason ) ) {
            $reason = 'Could not be updated automatically — please update manually.';
        }

        // Clean up raw file path names (e.g. perfmatters/perfmatters.php).
        $display_name = $r['name'] ?? $r['slug'] ?? '';
        if ( str_contains( $display_name, '/' ) || str_contains( $display_name, '.php' ) ) {
            $raw          = preg_replace( '/\.php$/', '', basename( $display_name ) );
            $display_name = ucwords( str_replace( [ '-', '_' ], ' ', $raw ) );
        }

        $rows_failed .= '<tr style="background:#fffbeb;">'
            . '<td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;">'
            . esc_html( $display_name )
            . '</td>'
            . '<td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;color:#6b7280;font-size:12px;">'
            . esc_html( $r['old_version'] ?? '' )
            . '</td>'
            . '<td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;">'
            . '<span style="color:#92400e;font-weight:700;">&#9888; ' . esc_html( $reason ) . '</span>'
            . '</td>'
            . '</tr>';
    }

    $success_section = '';
    if ( $rows_success ) {
        $success_section = '<h3 style="color:#166534;font-size:14px;margin:20px 0 8px;">&#10003; Completed Successfully</h3>'
            . '<table style="width:100%;border-collapse:collapse;font-size:13px;">'
            . '<thead><tr>'
            . '<th style="text-align:left;padding:8px 12px;background:#f0fdf4;border-bottom:2px solid #bbf7d0;">Plugin / Theme</th>'
            . '<th style="text-align:left;padding:8px 12px;background:#f0fdf4;border-bottom:2px solid #bbf7d0;">Version</th>'
            . '<th style="text-align:left;padding:8px 12px;background:#f0fdf4;border-bottom:2px solid #bbf7d0;">Status</th>'
            . '</tr></thead><tbody>' . $rows_success . '</tbody></table>';
    }

    $failed_section = '';
    if ( $rows_failed ) {
        $failed_section = '<h3 style="color:#92400e;font-size:14px;margin:20px 0 8px;">&#9888; Needs Attention</h3>'
            . '<table style="width:100%;border-collapse:collapse;font-size:13px;">'
            . '<thead><tr>'
            . '<th style="text-align:left;padding:8px 12px;background:#fffbeb;border-bottom:2px solid #fde68a;">Plugin / Theme</th>'
            . '<th style="text-align:left;padding:8px 12px;background:#fffbeb;border-bottom:2px solid #fde68a;">Current Version</th>'
            . '<th style="text-align:left;padding:8px 12px;background:#fffbeb;border-bottom:2px solid #fde68a;">Reason</th>'
            . '</tr></thead><tbody>' . $rows_failed . '</tbody></table>';
    }

    $log_url   = admin_url( 'admin.php?page=wpmm-update-log' );
    $email_url = admin_url( 'admin.php?page=wpmm-email-reports' );
    $timestamp = date_i18n( 'F j, Y \a\t g:i A' );

    $body = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head>'
        . '<body style="font-family:Arial,sans-serif;background:#f1f5f9;margin:0;padding:20px;">'
        . '<div style="max-width:640px;margin:0 auto;background:#fff;border-radius:8px;'
        .   'overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.1);">'
        . '<div style="background:#1e3a5f;padding:20px 28px;">'
        . '<p style="color:#fff;margin:0;font-size:18px;font-weight:bold;">&#9971; Greenskeeper</p>'
        . '<p style="color:#94a3b8;margin:4px 0 0;font-size:13px;">Update notification for ' . esc_html( $site_name ) . '</p>'
        . '</div>'
        . '<div style="padding:24px 28px;">'
        . '<p style="color:#6b7280;font-size:13px;margin:0 0 16px;">Batch completed on <strong>' . esc_html( $timestamp ) . '</strong>.</p>'
        . $success_section
        . $failed_section
        . '<div style="margin-top:24px;padding-top:16px;border-top:1px solid #e5e7eb;">'
        . '<a href="' . esc_url( $log_url ) . '" style="margin-right:16px;color:#1e3a5f;font-size:13px;">View Update Log &rarr;</a>'
        . ( $rows_success ? '<a href="' . esc_url( $email_url ) . '" style="color:#1e3a5f;font-size:13px;">Send Client Report &rarr;</a>' : '' )
        . '</div>'
        . '</div></div></body></html>';

    $headers = [
        'Content-Type: text/html; charset=UTF-8',
        'From: Greenskeeper <' . get_option( 'admin_email' ) . '>',
    ];

    wpmm_wp_mail( $to, $subject, $body, $headers );
}
