<?php
/*
Included File: Resource Links
Description: Allows admins to post external resource links for PaintOps.
Version: 1.0.2
Author: PaintOps
*/

if (!defined('ABSPATH')) {
    exit;
}

function paintops_register_resource_post_type() {
    $args = [
        'labels' => [
            'name' => 'Resources',
            'singular_name' => 'Resource',
            'add_new_item' => 'Add New Resource',
            'edit_item' => 'Edit Resource'
        ],
        'public' => false,
        'show_ui' => true,
        'show_in_menu' => false,
        'capability_type' => 'post',
        'capabilities' => [
            'create_posts' => 'manage_options',
        ],
        'map_meta_cap' => true,
        'supports' => ['title'],
    ];
    register_post_type('paintops_resource', $args);
}
add_action('init', 'paintops_register_resource_post_type');

function paintops_add_meta_boxes() {
    add_meta_box('paintops_resource_url', 'Resource URL', 'paintops_url_meta_box', 'paintops_resource', 'normal', 'default');
}
add_action('add_meta_boxes', 'paintops_add_meta_boxes');

function paintops_url_meta_box($post) {
    wp_nonce_field('paintops_save_resource_url', 'paintops_resource_nonce');
    $value = get_post_meta($post->ID, '_paintops_resource_url', true);
    echo '<input type="url" name="rl_url" style="width:100%" value="' . esc_attr($value) . '" required />';
}

function paintops_save_meta_box_data($post_id) {
    if (!isset($_POST['paintops_resource_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['paintops_resource_nonce'])), 'paintops_save_resource_url')) {
        return;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    if (array_key_exists('rl_url', $_POST)) {
        update_post_meta($post_id, '_paintops_resource_url', esc_url_raw(wp_unslash($_POST['rl_url'])));
    }
}
add_action('save_post_paintops_resource', 'paintops_save_meta_box_data');

/**
 * If a resource URL points at a YouTube or Vimeo video, return the
 * matching embeddable player URL so it can be dropped straight into an
 * <iframe>. Returns '' for anything else (a PDF, a supplier's site,
 * etc.), which just keeps behaving as a plain link.
 */
function paintops_get_embed_url(string $url): string {
    $url = trim($url);

    if ($url === '') {
        return '';
    }

    if (
        preg_match('~youtube\.com/watch\?v=([A-Za-z0-9_-]{6,})~i', $url, $m) ||
        preg_match('~youtube\.com/shorts/([A-Za-z0-9_-]{6,})~i', $url, $m) ||
        preg_match('~youtube\.com/embed/([A-Za-z0-9_-]{6,})~i', $url, $m) ||
        preg_match('~youtu\.be/([A-Za-z0-9_-]{6,})~i', $url, $m)
    ) {
        return 'https://www.youtube.com/embed/' . $m[1];
    }

    if (preg_match('~vimeo\.com/(?:video/)?(\d+)~i', $url, $m)) {
        return 'https://player.vimeo.com/video/' . $m[1];
    }

    return '';
}

/**
 * Seed a starter set of Resources so painters (especially new ones)
 * aren't looking at an empty list on day one. Runs once per site,
 * gated by the paintops_resources_seeded option, checked before this
 * even runs, and every seeded row is a normal paintops_resource post, so
 * it's just as editable or deletable as anything a user adds
 * themselves. If someone deletes one, it will not come back.
 *
 * Called from paintops_install() (see paintops.php), which itself
 * re-runs on every version bump via paintops_maybe_install_or_upgrade(),
 * so this reaches already-active sites too, not just fresh installs.
 */
function paintops_seed_default_resources(): void {
    if (get_option('paintops_resources_seeded')) {
        return;
    }

    $defaults = [
        ['title' => 'Best Way To Paint Doors With Sprayer', 'url' => 'https://www.youtube.com/watch?v=WuowOng-mZs'],
        ['title' => '3 Ways to Fix ANY Sized Drywall Hole', 'url' => 'https://www.youtube.com/watch?v=WWkNq_TGmhw'],
        ['title' => '5 Rules That Make or Break Painters', 'url' => 'https://www.youtube.com/watch?v=XLKxntL_0GQ'],
        ['title' => 'Multiple Useful Drywall Tips', 'url' => 'https://www.youtube.com/@vancouvercarpenter'],
        ['title' => '10 Steps Painting A Room FAST and EASY', 'url' => 'https://www.youtube.com/watch?v=Fz1hobau6IM'],
        ['title' => '12 Mistakes That Lose Sales', 'url' => 'https://www.youtube.com/watch?v=89zMhoNU880'],
        ['title' => 'OSHA: Chemicals and Other Hazards in Painting', 'url' => 'https://www.osha.gov/sites/default/files/2019-04/Chemicals_in_Painting_Materials.pdf'],
        ['title' => 'Getting the Most out of PaintOps', 'url' => 'https://paintoperations.com/getting-the-most-out-of-paintops/'],
        ['title' => 'PaintOps Recommended Tools & Supplies (may include affiliate links)', 'url' => 'https://paintoperations.com/paint-tools-supplies/'],
        ['title' => 'PaintOps Software Tutorial', 'url' => 'https://paintoperations.com/paintops-tutorial/'],
    ];

    foreach ($defaults as $item) {
        $post_id = wp_insert_post([
            'post_title'  => $item['title'],
            'post_type'   => 'paintops_resource',
            'post_status' => 'publish',
            'post_author' => get_current_user_id(),
        ]);

        if ($post_id && !is_wp_error($post_id)) {
            update_post_meta($post_id, '_paintops_resource_url', esc_url_raw($item['url']));
        }
    }

    update_option('paintops_resources_seeded', 1);
}

function paintops_display_resources() {
    if (isset($_POST['rl_submit_url']) && current_user_can('manage_options')) {
        check_admin_referer('paintops_add_resource', 'paintops_resource_form_nonce');

        $title = isset($_POST['rl_title']) ? sanitize_text_field(wp_unslash($_POST['rl_title'])) : '';
        $url = isset($_POST['rl_url']) ? esc_url_raw(wp_unslash($_POST['rl_url'])) : '';

        if ($title && $url) {
            $post_id = wp_insert_post([
                'post_title' => $title,
                'post_type' => 'paintops_resource',
                'post_status' => 'publish',
                'post_author' => get_current_user_id(),
            ]);

            if ($post_id && !is_wp_error($post_id)) {
                update_post_meta($post_id, '_paintops_resource_url', $url);
                echo '<div class="notice notice-success"><p>Resource added.</p></div>';
            }
        }
    }

    // Read-only display filters for the public resources list (a search
    // box and two sort links, like any WP search form). They change what
    // is SHOWN, never any state, and orderby/order are reduced to fixed
    // whitelists directly below; a nonce would only break bookmarked and
    // shared filter URLs. The state-changing branch above DOES verify its
    // nonce (check_admin_referer) plus manage_options before writing.
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $search_term = isset($_GET['rl_search']) ? sanitize_text_field(wp_unslash($_GET['rl_search'])) : '';
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $orderby = isset($_GET['rl_orderby']) ? sanitize_key(wp_unslash($_GET['rl_orderby'])) : 'date';
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $order = isset($_GET['rl_order']) ? strtoupper(sanitize_key(wp_unslash($_GET['rl_order']))) : 'DESC';

    if (!in_array($orderby, ['date', 'title'], true)) {
        $orderby = 'date';
    }

    if (!in_array($order, ['ASC', 'DESC'], true)) {
        $order = 'DESC';
    }

    $output = '';

    if (current_user_can('manage_options')) {
        $output .= '<button type="button" onclick="document.getElementById(\'rl-admin-form\').classList.toggle(\'hidden\');" class="paintops-btn paintops-btn-primary" style="margin-top:20px;margin-bottom:10px;">+ New Resource</button>';
        $output .= '<div id="rl-admin-form" class="hidden rl-admin-form-card"><form method="POST">';
        $output .= wp_nonce_field('paintops_add_resource', 'paintops_resource_form_nonce', true, false);
        $output .= '<input type="text" name="rl_title" placeholder="Title" required />';
        $output .= '<input type="url" name="rl_url" placeholder="https://example.com" required />';
        $output .= '<button type="submit" class="paintops-btn paintops-btn-primary" name="rl_submit_url" style="margin-top:6px;">Add Resource</button>';
        $output .= '</form></div>';
    }

    // AI resource suggestions, admin only, Pro + AI configured. Suggests
    // learning resources tailored to the shop's job history; every link is
    // a live search we build (no dead/hallucinated URLs), and admins can
    // save any pick as a permanent Resource.
    if (current_user_can('manage_options')
        && function_exists('paintops_ai_ready') && paintops_ai_ready()) {

        $rl_ai_cfg = wp_json_encode([
            'ajax'     => admin_url('admin-ajax.php'),
            'genNonce' => wp_create_nonce('paintops_ai_resource_suggestions'),
        ]);

        ob_start();
        ?>
var genBtn=document.getElementById('rl-ai-generate');
if(!genBtn){return;}
var panel=document.getElementById('rl-ai-panel');
var listEl=document.getElementById('rl-ai-list');
var statusEl=document.getElementById('rl-ai-status');
var loaded=false;
function esc(s){var d=document.createElement('div');d.textContent=(s==null?'':String(s));return d.innerHTML;}
var TYPE_LABELS={video:'Video',training:'Training',certification:'Certification',documentation:'Documentation',safety:'Safety'};
function render(items){
  listEl.innerHTML='';
  var order=['video','training','certification','documentation','safety'];
  var groups={};
  items.forEach(function(it){(groups[it.type]=groups[it.type]||[]).push(it);});
  order.forEach(function(t){
    var arr=groups[t];if(!arr||!arr.length){return;}
    var h=document.createElement('h4');h.className='rl-ai-group';h.textContent=TYPE_LABELS[t]||t;listEl.appendChild(h);
    arr.forEach(function(it){
      var card=document.createElement('div');card.className='rl-ai-card';
      var isVid=(t==='video');
      var primaryUrl=isVid?it.youtube_url:it.web_url;
      var primaryLabel=isVid?'▶ Search YouTube':'🔍 Search the web';
      var altUrl=isVid?it.web_url:it.youtube_url;
      var altLabel=isVid?'Web search':'YouTube';
      var src=it.source?(' <span class="rl-ai-src">'+esc(it.source)+'</span>'):'';
      card.innerHTML='<div class="rl-ai-card-h"><span class="rl-ai-badge rl-ai-badge-'+esc(t)+'">'+esc(TYPE_LABELS[t]||t)+'</span> <strong>'+esc(it.title)+'</strong>'+src+'</div>'+
        '<p class="rl-ai-why">'+esc(it.why)+'</p>'+
        '<div class="rl-ai-actions">'+
          '<a class="paintops-btn paintops-btn-secondary rl-ai-mini" target="_blank" rel="noopener" href="'+esc(primaryUrl)+'">'+primaryLabel+'</a>'+
          '<a class="rl-ai-altlink" target="_blank" rel="noopener" href="'+esc(altUrl)+'">'+altLabel+'</a>'+
        '</div>';
      listEl.appendChild(card);
    });
  });
}
function load(){
  loaded=true;statusEl.textContent='Reading your job history and thinking… up to 30s.';statusEl.className='rl-ai-status busy';listEl.innerHTML='';genBtn.disabled=true;
  var fd=new FormData();fd.append('action','paintops_ai_resource_suggestions');fd.append('nonce',CFG.genNonce);
  fetch(CFG.ajax,{method:'POST',body:fd,credentials:'same-origin'}).then(function(r){return r.json();}).then(function(res){
    genBtn.disabled=false;
    if(!res||!res.success){loaded=false;statusEl.className='rl-ai-status err';statusEl.textContent=(res&&res.data&&res.data.message)||'Something went wrong.';return;}
    var items=(res.data&&res.data.suggestions)||[];
    if(!items.length){loaded=false;statusEl.className='rl-ai-status err';statusEl.textContent='No suggestions came back. Try again.';return;}
    statusEl.className='rl-ai-status';statusEl.textContent='Based on your job categories and recent job titles. Reopen and it stays; use the button again for a fresh set.';
    render(items);
  }).catch(function(){genBtn.disabled=false;loaded=false;statusEl.className='rl-ai-status err';statusEl.textContent='Could not reach the server.';});
}
genBtn.addEventListener('click',function(){
  var isOpen=!panel.classList.contains('hidden');
  if(isOpen&&loaded){load();return;}
  panel.classList.remove('hidden');
  if(!loaded){load();}
});
        <?php
        $rl_ai_js = (string) ob_get_clean();

        $output .= '<div class="rl-ai-suggest-wrap">';
        $output .= '<button type="button" id="rl-ai-generate" class="paintops-btn paintops-btn-secondary" style="margin-top:20px;margin-bottom:6px;">&#10024; Get Suggestions from AI</button>';
        $output .= '<div id="rl-ai-panel" class="rl-ai-panel hidden">';
        $output .= '<p class="rl-ai-intro">Learning resources tailored to your job history: technique videos, training &amp; certifications, and safety/manufacturer docs. Each link opens a live search (never a dead URL). Found a good one? Add it with <strong>+ New Resource</strong> above so your crew can see it.</p>';
        $output .= '<div id="rl-ai-status" class="rl-ai-status"></div>';
        $output .= '<div id="rl-ai-list" class="rl-ai-list"></div>';
        $output .= '</div></div>';

        // Dynamic (nonce + ajax URL config) JS, routed through the
        // scripts API instead of a printed <script> tag; prints with the
        // footer scripts, which is fine since it only wires up click
        // handlers on the markup appended to $output above.
        paintops_add_inline_script('(function(){var CFG=' . $rl_ai_cfg . ';' . $rl_ai_js . '})();');
    }

    $output .= '<form method="GET" class="rl-toolbar">';

    if (is_admin()) {
        $output .= '<input type="hidden" name="page" value="' . esc_attr(isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : 'paintops-resources') . '" />';
    } else {
        $output .= '<input type="hidden" name="resources" value="1" />';
    }

    $output .= '<input type="text" name="rl_search" value="' . esc_attr($search_term) . '" placeholder="Search resources..." />';
    $output .= '<select name="rl_orderby">';
    $output .= '<option value="date"' . selected($orderby, 'date', false) . '>Date</option>';
    $output .= '<option value="title"' . selected($orderby, 'title', false) . '>Title</option>';
    $output .= '</select>';
    $output .= '<select name="rl_order">';
    $output .= '<option value="DESC"' . selected($order, 'DESC', false) . '>Descending</option>';
    $output .= '<option value="ASC"' . selected($order, 'ASC', false) . '>Ascending</option>';
    $output .= '</select>';
    $output .= '<button type="submit" class="button">Sort</button>';
    $output .= '</form>';

    $args = [
        'post_type' => 'paintops_resource',
        'post_status' => 'publish',
        'posts_per_page' => -1,
        'orderby' => $orderby,
        'order' => $order,
    ];

    if (!empty($search_term)) {
        $args['s'] = $search_term;
    }

    $query = new WP_Query($args);

    if (!$query->have_posts()) {
        return $output . '<p>No resources found.</p>';
    }

    $output .= '<ul class="rl-resource-list">';
    while ($query->have_posts()) {
        $query->the_post();
        $post_id = get_the_ID();
        $url = esc_url(get_post_meta($post_id, '_paintops_resource_url', true));
        $title = esc_html(get_the_title());
        $embed_url = paintops_get_embed_url($url);

        $embed_id = 'rl-embed-' . (int) $post_id;

        $output .= '<li>';

        // A video has its own "Play here" button to indicate it opens
        // inline, anything else (a supplier site, a PDF, etc.) gets a
        // plain link icon instead, so it is clear at a glance it just
        // opens the link.
        if ($embed_url === '') {
            $output .= '<span class="rl-link-icon" aria-hidden="true">&#128279;</span> ';
        }

        $output .= "<a href='{$url}' target='_blank' rel='noopener'>{$title}</a>";

        if ($embed_url !== '') {
            $output .= '<div class="rl-resource-meta">';
            $output .= "<button type='button' class='button button-small rl-play-toggle' data-target='{$embed_id}'>&#9654; Play here</button>";
            $output .= '</div>';
        }

        if ($embed_url !== '') {
            // No <iframe> is written here at all, just the URL, stashed
            // on the wrapper. The JS below creates a fresh <iframe> only
            // when "Play here" is clicked. An iframe with loading="lazy"
            // that starts out display:none (the .hidden class) never gets
            // picked up by the browser's lazy-load check, so it just sits
            // there forever the first time you open a second video,
            // building the <iframe> on click sidesteps that entirely.
            $output .= "<div id='{$embed_id}' class='rl-embed-wrap hidden' data-embed-src='" . esc_url($embed_url) . "'></div>";
        }

        $output .= '</li>';
    }
    $output .= '</ul>';
    wp_reset_postdata();

    return $output;
}
add_shortcode('paintops_resources', 'paintops_display_resources');

function paintops_resource_styles() {
    // Static CSS, now a bundled stylesheet through the styles API
    // (assets/css/resource-links.css). Enqueued from the same head hooks
    // as before; the enqueue helper handles the late timing (late styles
    // print with the footer).
    paintops_enqueue_style('resource-links');
}
add_action('wp_head', 'paintops_resource_styles');
add_action('admin_head', 'paintops_resource_styles');

/**
 * Click handler for the "Play here" buttons. Event-delegated on
 * document so it works no matter how many resource lists render on a
 * page, and printed once regardless of how many hooks fire it.
 */
function paintops_resource_scripts() {
    static $printed = false;

    if ($printed) {
        return;
    }
    $printed = true;

    // Static JS, now a bundled file through the scripts API
    // (assets/js/resource-links.js). wp_footer/admin_footer at default
    // priority still run before the footer scripts print, so enqueueing
    // here is not too late (and repeat enqueues no-op anyway).
    paintops_enqueue_script('resource-links');
}
add_action('wp_footer', 'paintops_resource_scripts');
add_action('admin_footer', 'paintops_resource_scripts');

function paintops_add_custom_columns($columns) {
    $columns['rl_url'] = 'URL';
    $columns['author'] = 'Posted By';
    $columns['date'] = 'Date Posted';
    return $columns;
}
add_filter('manage_paintops_resource_posts_columns', 'paintops_add_custom_columns');

function paintops_custom_column_content($column, $post_id) {
    if ($column === 'rl_url') {
        $url = get_post_meta($post_id, '_paintops_resource_url', true);
        echo wp_kses($url ? '<a href="' . esc_url($url) . '" target="_blank">' . esc_html($url) . '</a>' : '-', paintops_kses_allowed_html(), paintops_kses_protocols());
    }
}
add_action('manage_paintops_resource_posts_custom_column', 'paintops_custom_column_content', 10, 2);

function paintops_sortable_columns($columns) {
    $columns['rl_url'] = 'rl_url';
    return $columns;
}
add_filter('manage_edit-paintops_resource_sortable_columns', 'paintops_sortable_columns');

function paintops_orderby_custom_column($query) {
    if (!is_admin() || !$query->is_main_query()) {
        return;
    }
    if ($query->get('orderby') === 'rl_url') {
        $query->set('meta_key', '_paintops_resource_url');
        $query->set('orderby', 'meta_value');
    }
}
add_action('pre_get_posts', 'paintops_orderby_custom_column');
