<?php
/*
Plugin Name: Asana Form Builder
Description: Create dynamic forms in WordPress that send tasks to Asana.
Version: 1.27
Author: Paul Harvey
*/

define( 'AFB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );

//require_once WP_CONTENT_DIR . '/mu-plugins/shared-field-map-loader.php';


/*───────────────────────────────────────────────────────*
 * 1) FRONTEND SCRIPT + STYLE LOADING
 *───────────────────────────────────────────────────────*/
add_action( 'wp_enqueue_scripts', function () {

	/* Tailwind + custom styles (built via: npm run build) */
	$afb_css = plugin_dir_path( __FILE__ ) . 'assets/build/form.css';
	wp_enqueue_style(
		'afb-style',
		plugin_dir_url( __FILE__ ) . 'assets/build/form.css',
		[],
		file_exists( $afb_css ) ? filemtime( $afb_css ) : null
	);

    // Additional hand-authored UI styles (loads after build CSS)
    $afb_custom_css = plugin_dir_path( __FILE__ ) . 'assets/style.css';
    wp_enqueue_style(
        'afb-custom-style',
        plugin_dir_url( __FILE__ ) . 'assets/style.css',
        ['afb-style'],
        file_exists( $afb_custom_css ) ? filemtime( $afb_custom_css ) : null
    );

	/* ✅ Frappe-Gantt – load locally from plugin assets (in <head>) */
	wp_enqueue_style(
		'frappe-gantt-style',
		plugin_dir_url( __FILE__ ) . 'assets/frappe-gantt.css'
	);
	wp_enqueue_script(
		'frappe-gantt-js',
		plugin_dir_url( __FILE__ ) . 'assets/frappe-gantt.min.js',
		[],
		'0.6.0',
		false   // load in <head>
	);

    /* -------- Form logic (footer) -------- */

	// NEW: triage summary module (must load before form.js)
	$triage_rel = 'includes/functions/triagesummary/summary-builder.js';
	$triage_abs = plugin_dir_path(__FILE__) . $triage_rel;

	wp_enqueue_script(
	  'afb-triage-summary',
	  plugins_url($triage_rel, __FILE__),
	  array('jquery'),
	  file_exists($triage_abs) ? filemtime($triage_abs) : null,
	  true
	);

	wp_enqueue_script(
	  'afb-form',
	  plugins_url('assets/form.js', __FILE__),
      array('jquery','afb-triage-summary'),
	  filemtime( plugin_dir_path(__FILE__) . 'assets/form.js' ),
	  true
	);
	
	// Phased flow modules (create → upload → status)
wp_enqueue_script(
  'afb-flow-create',
  plugins_url('assets/js/flow/create.js', __FILE__),
  [],
  file_exists(plugin_dir_path(__FILE__).'assets/js/flow/create.js') ? filemtime(plugin_dir_path(__FILE__).'assets/js/flow/create.js') : null,
  true
);
wp_enqueue_script(
  'afb-flow-upload',
  plugins_url('assets/js/flow/upload.js', __FILE__),
  [],
  file_exists(plugin_dir_path(__FILE__).'assets/js/flow/upload.js') ? filemtime(plugin_dir_path(__FILE__).'assets/js/flow/upload.js') : null,
  true
);
wp_enqueue_script(
  'afb-flow-status',
  plugins_url('assets/js/flow/status.js', __FILE__),
  [],
  file_exists(plugin_dir_path(__FILE__).'assets/js/flow/status.js') ? filemtime(plugin_dir_path(__FILE__).'assets/js/flow/status.js') : null,
  true
);


	/* Leave guard – load EARLY so it can catch clicks before form.js finishes */
	wp_enqueue_script(
	    'afb-leave-guard-js',
	    plugin_dir_url( __FILE__ ) . 'assets/leave-guard.js',
	    [], // no dependency on form.js
	    filemtime( plugin_dir_path(__FILE__) . 'assets/leave-guard.js' ),
	    false // <-- HEAD
	);

	/* localize ONLY form.js */
    /* localize ONLY form.js */
    wp_localize_script( 'afb-form', 'afb_ajax', [
      'ajax_url'          => admin_url( 'admin-ajax.php' ),
            'home_url'          => home_url( '/' ),
      'prefix_map'        => afb_get_prefixes_map(),
      'field_flow_config' => afb_get_project_field_config(),
        'ai_rewrite_action' => 'afb_rewrite_single_input_copy',
        'ai_rewrite_nonce'  => wp_create_nonce( 'afb_ai_rewrite' ),
      'triage_state'      => function_exists( 'triage_gpt_get_state' )
                               ? triage_gpt_get_state()
                               : [],
    ] );
    $afb_global_field_map = function_exists( 'load_global_field_map' )
        ? load_global_field_map()
        : [];
    wp_localize_script( 'afb-form', 'GlobalFieldMap', $afb_global_field_map );

});    



/*───────────────────────────────────────────────────────*
 * 2) REWRITE RULE: map /asana-form/{gid}/ → page=asana-form
 *───────────────────────────────────────────────────────*/
add_action( 'init', function() {
    add_rewrite_rule(
        '^asana-form/([0-9A-Za-z_-]+)/?$',
        'index.php?pagename=asana-form&afb_form_project=$matches[1]',
        'top'
    );
});

add_filter( 'query_vars', function( $vars ) {
    $vars[] = 'afb_form_project';
    return $vars;
});

/*───────────────────────────────────────────────────────*
 * 3) UTILITIES                                           *
 *───────────────────────────────────────────────────────*/
function afb_get_prefixes_map() {
    $raw   = get_option( 'afb_project_prefix_map' );
    $lines = explode( "\n", trim( $raw ) );
    $map   = [];

    foreach ( $lines as $line ) {
        if ( strpos( $line, '=' ) !== false ) {
            list( $project_id, $prefix ) = array_map( 'trim', explode( '=', $line, 2 ) );
            $map[ $project_id ] = $prefix;
        }
    }
    return $map;
}

function afb_get_project_field_config() {
    $cfg = get_option( 'afb_project_field_config', [] );
    if ( ! is_array( $cfg ) ) {
        return [];
    }

    $normalize_string_list = static function ( $raw ) {
        $out = [];

        if ( is_array( $raw ) ) {
            foreach ( $raw as $key => $value ) {
                if ( is_string( $value ) && $value !== '' ) {
                    $out[] = $value;
                    continue;
                }

                if ( is_string( $key ) && $key !== '' && ! is_int( $key ) ) {
                    $out[] = $key;
                }
            }
        }

        return array_values( array_unique( array_filter( array_map( 'strval', $out ), static function ( $v ) {
            return $v !== '';
        } ) ) );
    };

    foreach ( $cfg as $project_id => $project_cfg ) {
        if ( ! is_array( $project_cfg ) ) {
            $cfg[ $project_id ] = [];
            continue;
        }

        foreach ( [
            'used_fields',
            'field_order',
            'required_fields',
            'agreement_required_fields',
            'agreement_description_fields',
            'submit_block_fields',
            'hidden_fields',
            'readonly_fields',
            'paragraph_fields',
            'half_width_fields',
            'third_width_fields',
            'custom_single_input_max_words_enabled',
            'custom_single_input_ai_enabled',
        ] as $list_key ) {
            $project_cfg[ $list_key ] = $normalize_string_list( $project_cfg[ $list_key ] ?? [] );
        }

        // Preserve legacy behavior: when this key is absent, upload blocks should
        // default to compression enabled rather than an explicit empty list.
        if ( array_key_exists( 'custom_upload_compress_images', $project_cfg ) ) {
            $project_cfg['custom_upload_compress_images'] = $normalize_string_list( $project_cfg['custom_upload_compress_images'] );
        }

        if ( ! empty( $project_cfg['half_width_fields'] ) && ! empty( $project_cfg['third_width_fields'] ) ) {
            $project_cfg['half_width_fields'] = array_values(
                array_diff( $project_cfg['half_width_fields'], $project_cfg['third_width_fields'] )
            );
        }

        $single_input_modes_raw = is_array( $project_cfg['custom_single_input_modes'] ?? null )
            ? $project_cfg['custom_single_input_modes']
            : [];
        $single_input_modes = [];
        foreach ( $single_input_modes_raw as $single_id => $single_mode ) {
            $single_id = is_string( $single_id ) ? trim( $single_id ) : '';
            if ( $single_id === '' ) {
                continue;
            }

            $mode = is_string( $single_mode ) ? trim( $single_mode ) : '';
            if ( ! in_array( $mode, [ 'text', 'textarea' ], true ) ) {
                $mode = 'text';
            }

            $single_input_modes[ $single_id ] = $mode;
        }
        $project_cfg['custom_single_input_modes'] = $single_input_modes;

        $single_input_max_words_raw = is_array( $project_cfg['custom_single_input_max_words'] ?? null )
            ? $project_cfg['custom_single_input_max_words']
            : [];
        $single_input_max_words = [];
        foreach ( $single_input_max_words_raw as $single_id => $max_words ) {
            $single_id = is_string( $single_id ) ? trim( $single_id ) : '';
            if ( $single_id === '' ) {
                continue;
            }

            $single_input_max_words[ $single_id ] = max( 0, absint( $max_words ) );
        }
        $project_cfg['custom_single_input_max_words'] = $single_input_max_words;

        $repeater_save_modes_raw = is_array( $project_cfg['custom_repeater_save_mode'] ?? null )
            ? $project_cfg['custom_repeater_save_mode']
            : [];
        $repeater_save_modes = [];
        foreach ( $repeater_save_modes_raw as $repeater_id => $save_mode ) {
            $repeater_id = is_string( $repeater_id ) ? trim( $repeater_id ) : '';
            if ( $repeater_id === '' ) {
                continue;
            }

            $mode = is_string( $save_mode ) ? trim( $save_mode ) : '';
            $repeater_save_modes[ $repeater_id ] = ( $mode === 'description' ) ? 'description' : 'subtasks';
        }
        $project_cfg['custom_repeater_save_mode'] = $repeater_save_modes;

        $upload_max_file_mb_raw = is_array( $project_cfg['custom_upload_max_file_mb'] ?? null )
            ? $project_cfg['custom_upload_max_file_mb']
            : [];
        $upload_max_file_mb = [];
        foreach ( $upload_max_file_mb_raw as $upload_id => $max_file_mb ) {
            $upload_id = is_string( $upload_id ) ? trim( $upload_id ) : '';
            if ( $upload_id === '' || strpos( $upload_id, 'upload_' ) !== 0 ) {
                continue;
            }

            $max_mb = (float) $max_file_mb;
            if ( $max_mb <= 0 ) {
                continue;
            }

            $upload_max_file_mb[ $upload_id ] = min( 512, round( $max_mb, 2 ) );
        }
        $project_cfg['custom_upload_max_file_mb'] = $upload_max_file_mb;

        $upload_max_width_px_raw = is_array( $project_cfg['custom_upload_max_width_px'] ?? null )
            ? $project_cfg['custom_upload_max_width_px']
            : [];
        $upload_max_width_px = [];
        foreach ( $upload_max_width_px_raw as $upload_id => $max_width ) {
            $upload_id = is_string( $upload_id ) ? trim( $upload_id ) : '';
            if ( $upload_id === '' || strpos( $upload_id, 'upload_' ) !== 0 ) {
                continue;
            }

            $max_width_num = (float) $max_width;
            if ( $max_width_num <= 0 ) {
                continue;
            }

            $upload_max_width_px[ $upload_id ] = max( 100, min( 4000, (int) round( $max_width_num ) ) );
        }
        $project_cfg['custom_upload_max_width_px'] = $upload_max_width_px;

        $upload_compress_quality_raw = is_array( $project_cfg['custom_upload_compress_quality'] ?? null )
            ? $project_cfg['custom_upload_compress_quality']
            : [];
        $upload_compress_quality = [];
        foreach ( $upload_compress_quality_raw as $upload_id => $quality ) {
            $upload_id = is_string( $upload_id ) ? trim( $upload_id ) : '';
            if ( $upload_id === '' || strpos( $upload_id, 'upload_' ) !== 0 ) {
                continue;
            }

            $quality_num = (float) $quality;
            if ( $quality_num <= 0 ) {
                continue;
            }

            $upload_compress_quality[ $upload_id ] = max( 1, min( 100, (int) round( $quality_num ) ) );
        }
        $project_cfg['custom_upload_compress_quality'] = $upload_compress_quality;

        $calendar_preset_modes = is_array( $project_cfg['custom_calendar_preset_modes'] ?? null )
            ? $project_cfg['custom_calendar_preset_modes']
            : [];

        $calendar_preset_options = [];
        foreach ( $calendar_preset_modes as $calendar_id => $preset_mode ) {
            $calendar_id = is_string( $calendar_id ) ? trim( $calendar_id ) : '';
            if ( $calendar_id === '' ) {
                continue;
            }

            $mode = is_string( $preset_mode ) ? trim( $preset_mode ) : '';
            if ( $mode !== 'ealert_next_4_wednesdays' ) {
                continue;
            }

            if ( function_exists( 'triage_ealert_date_options' ) ) {
                $rows = triage_ealert_date_options( 4, null, 2 );
                if ( is_array( $rows ) ) {
                    $calendar_preset_options[ $calendar_id ] = array_values( array_filter( array_map( 'strval', $rows ), static function ( $v ) {
                        return $v !== '';
                    } ) );
                }
            }
        }

        $project_cfg['custom_calendar_preset_options'] = $calendar_preset_options;

        $cfg[ $project_id ] = $project_cfg;
    }

    return $cfg;
}

/*───────────────────────────────────────────────────────*
 * 4) CORE INCLUDES                                       *
 *───────────────────────────────────────────────────────*/
// Lightweight admin-only timing instrumentation: add `?afb_perf=1` to admin URLs
$__afb_perf_enabled = (bool) (is_admin() && isset($_GET['afb_perf']));
if ($__afb_perf_enabled) {
    $__afb_perf_prev = microtime(true);
    $__afb_perf_log = function ($label) use (&$__afb_perf_prev) {
        $now = microtime(true);
        $ms = (int) round(($now - $__afb_perf_prev) * 1000);
        @file_put_contents(WP_CONTENT_DIR . '/afb-admin-timings.log', date('c') . " {$label} +{$ms}ms\n", FILE_APPEND);
        $__afb_perf_prev = $now;
    };
}

if ($__afb_perf_enabled) $__afb_perf_log('before admin/ajax-handlers.php');
require_once AFB_PLUGIN_DIR . 'admin/ajax-handlers.php';
if ($__afb_perf_enabled) $__afb_perf_log('after admin/ajax-handlers.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/api.php');
require_once AFB_PLUGIN_DIR . 'includes/api.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/api.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/task-handler.php');
require_once AFB_PLUGIN_DIR . 'includes/task-handler.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/task-handler.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/shortcodes.php');
require_once AFB_PLUGIN_DIR . 'includes/shortcodes.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/shortcodes.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/email-sender.php');
require_once AFB_PLUGIN_DIR . 'includes/email-sender.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/email-sender.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/security.php');
require_once AFB_PLUGIN_DIR . 'includes/security.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/security.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/lib/status-store.php');
require_once AFB_PLUGIN_DIR . 'includes/lib/status-store.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/lib/status-store.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/lib/asana-client.php');
require_once AFB_PLUGIN_DIR . 'includes/lib/asana-client.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/lib/asana-client.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/api/create-task.php');
require_once AFB_PLUGIN_DIR . 'includes/api/create-task.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/api/create-task.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/api/attach-file.php');
require_once AFB_PLUGIN_DIR . 'includes/api/attach-file.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/api/attach-file.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/api/status.php');
require_once AFB_PLUGIN_DIR . 'includes/api/status.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/api/status.php');

if ($__afb_perf_enabled) $__afb_perf_log('before includes/jobs/enrich-task.php');
require_once AFB_PLUGIN_DIR . 'includes/jobs/enrich-task.php';
if ($__afb_perf_enabled) $__afb_perf_log('after includes/jobs/enrich-task.php');

if ($__afb_perf_enabled) $__afb_perf_log('before modules/automator/asana-automator.php');
require_once AFB_PLUGIN_DIR . 'modules/automator/asana-automator.php';
if ($__afb_perf_enabled) $__afb_perf_log('after modules/automator/asana-automator.php');



if ( is_admin() ) {
    require_once AFB_PLUGIN_DIR . 'admin/menu.php';

    add_action( 'admin_init', function () {
        register_setting( 'afb_general_settings_group', 'afb_selected_portfolio' );
    } );
}

/*───────────────────────────────────────────────────────*
 * 5) AJAX ENDPOINT: GET PROJECT FIELDS
 *───────────────────────────────────────────────────────*/
add_action( 'wp_ajax_afb_get_project_fields',       'afb_handle_get_project_fields' );
add_action( 'wp_ajax_nopriv_afb_get_project_fields','afb_handle_get_project_fields' );

function afb_handle_get_project_fields() {
    try {
        /* ---------- sanity ---------- */
        if ( empty( $_POST['project_id'] ) ) {
            wp_send_json_error( [ 'message' => 'No project ID provided.' ] );
        }
        $project_id = sanitize_text_field( $_POST['project_id'] );

        /* ---------- 1. PAT sent from JS (preferred) ---------- */
        $raw_pat = isset( $_POST['pat'] ) ? sanitize_text_field( $_POST['pat'] ) : '';
        $pat     = $raw_pat ? afb_decrypt( $raw_pat ) ?: $raw_pat : null;   // decrypt if needed

        /* ---------- 2. Fallback – look up PAT by project ---------- */
        if ( empty( $pat ) ) {
            global $wpdb;
            $acct_tbl = $wpdb->prefix . 'asana_accounts';
            $accounts = $wpdb->get_results( "SELECT * FROM $acct_tbl WHERE active = 1", ARRAY_A );

            foreach ( $accounts as $acc ) {
                $token     = afb_decrypt( $acc['pat'] );
                $portfolio = $acc['portfolio_gid'] ?: null;
                $projs     = $portfolio
                    ? afb_get_portfolio_projects( $portfolio, $token )
                    : afb_fetch_asana( 'projects', $token );

                foreach ( $projs as $p ) {
                    if ( $p['gid'] === $project_id ) { $pat = $token; break 2; }
                }
            }
        }

        /* ---------- 3. Fetch fields with the resolved PAT ---------- */
        $all_fields = afb_get_project_fields( $project_id, $pat );
        if ( $all_fields === false ) {
            wp_send_json_error( [ 'message' => 'Failed to fetch fields from Asana.' ] );
        }

        /* ---------- 4. Optional field-flow filtering ---------- */
        $cfg     = afb_get_project_field_config();
        $allowed = $cfg[ $project_id ]['used_fields'] ?? [];

        $fields  = empty( $allowed )
            ? $all_fields
            : array_values( array_filter( $all_fields,
                    fn( $s ) => in_array( $s['custom_field']['gid'], $allowed, true ) ) );

        wp_send_json_success( [ 'fields' => $fields ] );

    } catch ( \Throwable $e ) {
        error_log( 'Error in afb_handle_get_project_fields: ' . $e->getMessage() );
        wp_send_json_error( [ 'message' => 'Server error; check debug log.' ] );
    }
}

// ── NEW: packetised submission endpoints ──────────────────────────────
add_action('wp_ajax_afb_create_task_fast',      'afb_api_create_task_fast');
add_action('wp_ajax_nopriv_afb_create_task_fast','afb_api_create_task_fast');

add_action('wp_ajax_afb_attach_file',          'afb_api_attach_file');
add_action('wp_ajax_nopriv_afb_attach_file',   'afb_api_attach_file');

add_action('wp_ajax_afb_status',               'afb_api_status');
add_action('wp_ajax_nopriv_afb_status',        'afb_api_status');



/*───────────────────────────────────────────────────────*
 * 6) TASK COMMS MODULE + POST TYPE INTEGRATIONS          *
 *───────────────────────────────────────────────────────*/
require_once plugin_dir_path(__FILE__) . 'task-comms/loader.php';
require_once plugin_dir_path(__FILE__) . 'task-comms/routes.php';
require_once plugin_dir_path(__FILE__) . 'task-comms/functions.php';
require_once plugin_dir_path(__FILE__) . 'task-comms/ajax-post-comment.php';
require_once plugin_dir_path(__FILE__) . 'task-comms/ajax-save-settings.php';
require_once plugin_dir_path( __FILE__ ) . 'task-comms/rest-create-dashboard.php';
require_once plugin_dir_path( __FILE__ ) . 'task-comms/router-create-dashboard.php';
require_once plugin_dir_path(__FILE__) . 'task-comms/partials/handlers/email-copy-ajax.php';



register_activation_hook(__FILE__, function () {
    if (!wp_next_scheduled('afb_cleanup_cron_hook')) {
        wp_schedule_event(time(), 'twicedaily', 'afb_cleanup_cron_hook');
    }
    if (!wp_next_scheduled('afb_gateway_completion_cron_hook')) {
        wp_schedule_event(time(), 'hourly', 'afb_gateway_completion_cron_hook');
    }
    if (defined('ASANA_AUTOMATOR_CRON_HOOK') && !wp_next_scheduled(ASANA_AUTOMATOR_CRON_HOOK)) {
        wp_schedule_event(time() + 60, 'five_minutes', ASANA_AUTOMATOR_CRON_HOOK);
    }
});

register_deactivation_hook(__FILE__, function () {
    wp_clear_scheduled_hook('afb_cleanup_cron_hook');
    wp_clear_scheduled_hook('afb_gateway_completion_cron_hook');
    wp_clear_scheduled_hook('asana_automator_cron_dispatch');
    wp_clear_scheduled_hook('asana_automator_run');
});

add_action('init', function () {
    if (!wp_next_scheduled('afb_gateway_completion_cron_hook')) {
        wp_schedule_event(time() + 120, 'hourly', 'afb_gateway_completion_cron_hook');
    }
});

add_action( 'wp_enqueue_scripts', 'afb_enqueue_comment_script' );
function afb_enqueue_comment_script() {

    // this query-var exists because of your rewrite/route
    $token = get_query_var( 'asana_comms_token', false );

    if ( $token ) {
        wp_enqueue_script(
            'afb-comments',
            plugins_url( 'assets/js/comments.js', __FILE__ ), // ← safer helper
            [],            // no dependencies
            null,          // no version (use filemtime() if you want cache-busting)
            true           // load in footer
        );

        wp_localize_script( 'afb-comments', 'afb_ajax', [
            'ajax_url' => admin_url( 'admin-ajax.php' ),
        ]);
    }
}




if (!function_exists('afb_log')) {
    /**
     * Log debug messages to a custom plugin log file.
     *
     * @param mixed  $message  The message or data to log.
     * @param string $label    Optional label to prefix the log entry.
     */
    function afb_log($message, $label = 'DEBUG') {
        $log_file = WP_CONTENT_DIR . '/afb-debug.log';
        $timestamp = date('Y-m-d H:i:s');
        $output = is_array($message) || is_object($message)
            ? print_r($message, true)
            : $message;

        error_log("[{$timestamp}] {$label}: {$output}\n", 3, $log_file);
    }
}


