<?php
/**
 * Task Comms – Submit Client Feedback
 *
 * Handles post-completion feedback submission (rating, reply, notes)
 * Writes to Asana custom fields and then expires the gateway.
 */

add_action('wp_ajax_afb_submit_feedback', 'afb_submit_feedback');
add_action('wp_ajax_nopriv_afb_submit_feedback', 'afb_submit_feedback');

if (!function_exists('afb_gateway_request_ip')) {
    function afb_gateway_request_ip() {
        $raw = (string) ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
        return sanitize_text_field($raw ?: 'unknown');
    }
}

if (!function_exists('afb_gateway_rate_limit_hit')) {
    function afb_gateway_rate_limit_hit($bucket, $max_attempts, $window_seconds) {
        $key = 'afb_rl_' . md5((string) $bucket);
        $hits = (int) get_transient($key);
        $hits++;
        set_transient($key, $hits, (int) $window_seconds);
        return $hits > (int) $max_attempts;
    }
}

if (!function_exists('afb_gateway_validate_token_task_pair')) {
    function afb_gateway_validate_token_task_pair($gateway_token, $task_gid) {
        $gateway_token = sanitize_text_field((string) $gateway_token);
        $task_gid = sanitize_text_field((string) $task_gid);

        if ($gateway_token === '' || $task_gid === '' || !ctype_digit($task_gid)) {
            return false;
        }

        $mapped = (string) afb_get_task_id_by_token($gateway_token);
        return ($mapped !== '' && hash_equals($mapped, $task_gid));
    }
}

if (!function_exists('afb_feedback_get_token_candidates')) {
    function afb_feedback_get_token_candidates() {
        global $wpdb;

        $candidates = [];

        $push_candidate = static function ($raw_token) use (&$candidates) {
            if (!is_string($raw_token)) {
                return;
            }

            $raw_token = trim($raw_token);
            if ($raw_token === '') {
                return;
            }

            $candidates[] = $raw_token;

            if (function_exists('afb_decrypt')) {
                $decrypted = (string) afb_decrypt($raw_token);
                $decrypted = trim($decrypted);
                if ($decrypted !== '') {
                    $candidates[] = $decrypted;
                }
            }
        };

        // Highest priority: PATs in asana_accounts DB table (active first), with decrypt fallback.
        if (isset($wpdb) && $wpdb instanceof wpdb) {
            $table = $wpdb->prefix . 'asana_accounts';
            $rows = $wpdb->get_results("SELECT pat FROM {$table} ORDER BY active DESC, id DESC", ARRAY_A);
            if (is_array($rows)) {
                foreach ($rows as $row) {
                    $push_candidate($row['pat'] ?? '');
                }
            }
        }

        if (function_exists('afb_get_active_pat_token')) {
            $active_pat = (string) afb_get_active_pat_token();
            if ($active_pat !== '') {
                $push_candidate($active_pat);
            }
        }

        $stored = get_option('asana_accounts');
        $accounts = [];

        if (is_array($stored)) {
            $accounts = $stored;
        } elseif (is_string($stored) && is_serialized($stored)) {
            $unserialized = maybe_unserialize($stored);
            if (is_array($unserialized)) {
                $accounts = $unserialized;
            }
        }

        if (is_array($accounts)) {
            foreach ($accounts as $maybe_pat) {
                $push_candidate($maybe_pat);
            }
        }

        $legacy_pat = get_option('asana_access_token');
        if (is_string($legacy_pat) && trim($legacy_pat) !== '') {
            $push_candidate($legacy_pat);
        }

        return array_values(array_unique(array_filter($candidates, function ($pat) {
            return is_string($pat) && $pat !== '';
        })));
    }
}

if (!function_exists('afb_feedback_pick_task_token')) {
    function afb_feedback_pick_task_token($task_gid, array $token_candidates) {
        $task_gid = sanitize_text_field((string) $task_gid);
        if ($task_gid === '' || !ctype_digit($task_gid)) {
            return '';
        }

        foreach ($token_candidates as $candidate_token) {
            if (!is_string($candidate_token) || trim($candidate_token) === '') {
                continue;
            }

            $response = wp_remote_get(
                "https://app.asana.com/api/1.0/tasks/{$task_gid}?opt_fields=gid",
                [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $candidate_token,
                    ],
                    'timeout' => 8,
                ]
            );

            if (!is_wp_error($response) && (int) wp_remote_retrieve_response_code($response) === 200) {
                return (string) $candidate_token;
            }
        }

        return '';
    }
}

if (!function_exists('afb_feedback_asana_request_with_fallback')) {
    function afb_feedback_asana_request_with_fallback($url, $method, $data, array $token_candidates, array $success_codes = [200]) {
        $last_result = [
            'success' => false,
            'code' => 0,
            'body' => '',
            'token' => '',
            'response' => null,
        ];

        foreach ($token_candidates as $candidate_token) {
            $args = [
                'method'  => strtoupper((string) $method),
                'headers' => [
                    'Authorization' => 'Bearer ' . $candidate_token,
                    'Content-Type'  => 'application/json',
                ],
            ];

            if (is_array($data)) {
                $args['body'] = json_encode(['data' => $data]);
            }

            $response = wp_remote_request($url, $args);

            if (is_wp_error($response)) {
                $last_result = [
                    'success' => false,
                    'code' => 0,
                    'body' => $response->get_error_message(),
                    'token' => (string) $candidate_token,
                    'response' => $response,
                ];
                continue;
            }

            $code = (int) wp_remote_retrieve_response_code($response);
            $body = (string) wp_remote_retrieve_body($response);

            if (in_array($code, $success_codes, true)) {
                return [
                    'success' => true,
                    'code' => $code,
                    'body' => $body,
                    'token' => (string) $candidate_token,
                    'response' => $response,
                ];
            }

            $last_result = [
                'success' => false,
                'code' => $code,
                'body' => $body,
                'token' => (string) $candidate_token,
                'response' => $response,
            ];

            // Retry with next token only for auth/object-visibility style failures.
            if (!in_array($code, [401, 403, 404], true)) {
                break;
            }
        }

        return $last_result;
    }
}

if (!function_exists('afb_feedback_request_for_task')) {
    function afb_feedback_request_for_task($task_gid, $url, $method, $data, array $token_candidates, array $success_codes = [200], $preferred_token = '') {
        $task_gid = sanitize_text_field((string) $task_gid);
        $preferred_token = is_string($preferred_token) ? trim($preferred_token) : '';
        $normalized_data = is_array($data) ? $data : null;

        if (function_exists('afb_asana_request_for_task')) {
            $primary = afb_asana_request_for_task(
                $task_gid,
                $url,
                $method,
                $normalized_data,
                $success_codes,
                $preferred_token
            );

            if (!empty($primary['success'])) {
                return $primary;
            }

            // For definitive validation-style failures, do not continue retrying with local fallback.
            if (!in_array((int) ($primary['code'] ?? 0), [0, 401, 403, 404], true)) {
                return $primary;
            }
        }

        if ($preferred_token !== '') {
            array_unshift($token_candidates, $preferred_token);
        }

        $token_candidates = array_values(array_unique(array_filter($token_candidates, function ($token) {
            return is_string($token) && trim($token) !== '';
        })));

        if (empty($token_candidates)) {
            return [
                'success' => false,
                'code' => 0,
                'body' => 'No Asana token candidates available',
                'token' => '',
                'response' => null,
            ];
        }

        return afb_feedback_asana_request_with_fallback(
            $url,
            $method,
            $normalized_data,
            $token_candidates,
            $success_codes
        );
    }
}

function afb_submit_feedback() {
    // STEP 1: Validate and prepare request
    $task_gid      = sanitize_text_field($_POST['task_gid'] ?? '');
    $gateway_token = sanitize_text_field($_POST['gateway_token'] ?? '');
    $completion_decision = sanitize_key((string) ($_POST['completion_decision'] ?? 'complete'));
    $rating        = (int) ($_POST['rating'] ?? 0);
    $reply         = sanitize_textarea_field($_POST['reply'] ?? '');
    $feedback      = sanitize_textarea_field($_POST['feedback'] ?? '');
    $not_complete_reason = sanitize_textarea_field($_POST['not_complete_reason'] ?? '');

    if (!in_array($completion_decision, ['complete', 'not_complete'], true)) {
        $completion_decision = 'complete';
    }

    if (!$task_gid) {
        wp_send_json_error(['message' => 'Missing task ID.']);
    }

    if (!afb_gateway_validate_token_task_pair($gateway_token, $task_gid)) {
        wp_send_json_error(['message' => 'Invalid gateway token/task pair.']);
    }

    // STEP 2: Resolve Asana token candidates (task-scoped cache first, then global fallbacks)
    $token_candidates = afb_feedback_get_token_candidates();
    $cached_task_token = (string) get_transient('afb_task_pat_' . md5($task_gid));

    if ($cached_task_token !== '') {
        array_unshift($token_candidates, $cached_task_token);
    }

    if (function_exists('afb_get_asana_task_pat_candidates')) {
        $task_scoped_candidates = afb_get_asana_task_pat_candidates($task_gid, $cached_task_token);
        if (is_array($task_scoped_candidates) && !empty($task_scoped_candidates)) {
            $token_candidates = array_merge($task_scoped_candidates, $token_candidates);
        }
    }

    $token_candidates = array_values(array_unique(array_filter($token_candidates, function ($token) {
        return is_string($token) && trim($token) !== '';
    })));

    if (empty($token_candidates)) {
        wp_send_json_error(['message' => 'Asana configuration missing. Please contact support.']);
    }

    // Resolve the exact PAT that can access this task, then prioritize it for all writes.
    $task_token = afb_feedback_pick_task_token($task_gid, $token_candidates);
    if ($task_token === '' && $cached_task_token !== '') {
        $task_token = $cached_task_token;
    }

    if ($task_token === '') {
        afb_debug_log('❌ Feedback token resolution failed | task=' . $task_gid . ' candidates=' . count($token_candidates));
        wp_send_json_error(['message' => 'This task could not be accessed with current Asana accounts. Please contact support.']);
    }

    $token_candidates = array_values(array_unique(array_filter(
        array_merge([$task_token], $token_candidates),
        function ($token) {
            return is_string($token) && $token !== '';
        }
    )));

    if ($completion_decision === 'not_complete') {
        if ($not_complete_reason === '') {
            wp_send_json_error(['message' => 'Please tell us what is still outstanding.']);
        }
    } else {
        // At least one piece of feedback required for completed jobs
        if ($rating === 0 && $reply === '' && $feedback === '') {
            wp_send_json_error(['message' => 'Please provide at least a rating or feedback.']);
        }
    }

    // STEP 3: Rate limiting
    $ip = afb_gateway_request_ip();
    $rl_bucket = 'feedback|' . $gateway_token . '|' . $ip;
    if (afb_gateway_rate_limit_hit($rl_bucket, 5, 3600)) {
        wp_send_json_error(['message' => 'Too many feedback submissions. Please try again later.']);
    }

    // STEP 4A: If client says not complete, reopen task and add reason as a story comment
    if ($completion_decision === 'not_complete') {
        $reopen = afb_feedback_request_for_task(
            $task_gid,
            "https://app.asana.com/api/1.0/tasks/{$task_gid}",
            'PUT',
            ['completed' => false],
            $token_candidates,
            [200],
            $task_token
        );

        if (!$reopen['success']) {
            $reopen_token = (string) ($reopen['token'] ?? '');
            $reopen_token_masked = $reopen_token !== ''
                ? (substr($reopen_token, 0, 4) . '...' . substr($reopen_token, -4))
                : 'none';
            afb_debug_log("❌ Task reopen failed | task={$task_gid} http={$reopen['code']} token={$reopen_token_masked} candidates=" . count($token_candidates) . " body={$reopen['body']}");
            wp_send_json_error(['message' => 'Failed to reopen this task. Please try again.']);
        }

        if (!empty($reopen['token'])) {
            set_transient('afb_task_pat_' . md5($task_gid), (string) $reopen['token'], DAY_IN_SECONDS);
        }

        $token_for_story = !empty($reopen['token']) ? [$reopen['token']] : $token_candidates;

        $story_text = "[Client Update] Job marked as not complete. Reason: {$not_complete_reason}";
        $story = afb_feedback_request_for_task(
            $task_gid,
            "https://app.asana.com/api/1.0/tasks/{$task_gid}/stories",
            'POST',
            ['text' => $story_text],
            $token_for_story,
            [200, 201],
            $task_token
        );

        if (!$story['success']) {
            afb_debug_log("⚠️ Story note failed (non-fatal) | task={$task_gid} http={$story['code']}");
        }

        afb_debug_log("✅ Task marked not complete by client | token={$gateway_token} task={$task_gid}");

        wp_send_json_success([
            'mode' => 'not_complete',
            'message' => "Thanks for the update. We've marked this job as not complete and shared your note with the team.",
        ]);
    }

    // STEP 4B: Get feedback field GIDs from settings
    $feedback_fields = afb_get_feedback_field_gids();
    if (empty($feedback_fields)) {
        wp_send_json_error(['message' => 'Feedback fields are not configured. Please contact support.']);
    }

    // STEP 5: Build custom fields update
    $custom_fields_update = [];

    if (!empty($feedback_fields['rating_gid']) && $rating > 0) {
        // Rating is stored as an enum; map the integer (1-5) to the corresponding enum option GID
        $enum_gids = $feedback_fields['rating_enum_gids'] ?? [];
        $enum_option_gid = $enum_gids[$rating - 1] ?? '';
        if ($enum_option_gid !== '') {
            $custom_fields_update[$feedback_fields['rating_gid']] = $enum_option_gid;
        }
    }

    if (!empty($feedback_fields['reply_gid']) && $reply !== '') {
        $custom_fields_update[$feedback_fields['reply_gid']] = $reply;
    }

    if (!empty($feedback_fields['feedback_gid']) && $feedback !== '') {
        $custom_fields_update[$feedback_fields['feedback_gid']] = $feedback;
    }

    // Status field is an enum; write the "Submitted" enum option GID (not text)
    if (!empty($feedback_fields['status_gid']) && !empty($feedback_fields['status_submitted_gid'])) {
        $custom_fields_update[$feedback_fields['status_gid']] = $feedback_fields['status_submitted_gid'];
    }

    // Clear gateway URL + PIN fields once feedback is submitted and the gateway closes.
    $gateway_link_gid = function_exists('afb_get_gateway_client_link_field_gid')
        ? sanitize_text_field((string) afb_get_gateway_client_link_field_gid())
        : sanitize_text_field((string) get_option('afb_client_gateway_link_field_gid', '1211304630299018'));

    $gateway_pin_gid = function_exists('afb_get_gateway_client_pin_field_gid')
        ? sanitize_text_field((string) afb_get_gateway_client_pin_field_gid())
        : sanitize_text_field((string) get_option('afb_client_gateway_pin_field_gid', '1214258543781835'));

    $gateway_clear_gids = [];
    if ($gateway_link_gid !== '' && ctype_digit($gateway_link_gid)) {
        $gateway_clear_gids[] = $gateway_link_gid;
    }
    if ($gateway_pin_gid !== '' && ctype_digit($gateway_pin_gid)) {
        $gateway_clear_gids[] = $gateway_pin_gid;
    }

    $gateway_clear_gids = array_values(array_unique($gateway_clear_gids));
    foreach ($gateway_clear_gids as $gateway_field_gid) {
        $custom_fields_update[$gateway_field_gid] = '';
    }

    // STEP 6: Update Asana task with feedback custom fields
    $update = afb_feedback_request_for_task(
        $task_gid,
        "https://app.asana.com/api/1.0/tasks/{$task_gid}",
        'PUT',
        ['custom_fields' => $custom_fields_update],
        $token_candidates,
        [200],
        $task_token
    );

    // If gateway field mapping is stale on this task, retry once without the unavailable gateway fields.
    if (!$update['success'] && (int) ($update['code'] ?? 0) === 400 && !empty($gateway_clear_gids)) {
        $update_body = (string) ($update['body'] ?? '');
        $unavailable_gateway_fields = [];

        foreach ($gateway_clear_gids as $gateway_field_gid) {
            if (strpos($update_body, "Custom field with ID {$gateway_field_gid} is not on given object") !== false) {
                $unavailable_gateway_fields[] = $gateway_field_gid;
                unset($custom_fields_update[$gateway_field_gid]);
            }
        }

        if (!empty($unavailable_gateway_fields)) {
            afb_debug_log(
                'ℹ️ Feedback update retry without unavailable gateway fields | task=' . $task_gid
                . ' gids=' . implode(',', $unavailable_gateway_fields)
            );

            $update = afb_feedback_request_for_task(
                $task_gid,
                "https://app.asana.com/api/1.0/tasks/{$task_gid}",
                'PUT',
                ['custom_fields' => $custom_fields_update],
                $token_candidates,
                [200],
                $task_token
            );
        }
    }

    if (!$update['success']) {
        $update_token = (string) ($update['token'] ?? '');
        $update_token_masked = $update_token !== ''
            ? (substr($update_token, 0, 4) . '...' . substr($update_token, -4))
            : 'none';
        afb_debug_log("❌ Feedback update failed | task={$task_gid} http={$update['code']} token={$update_token_masked} candidates=" . count($token_candidates) . " body={$update['body']}");
        wp_send_json_error(['message' => 'Failed to save feedback. Please try again.']);
    }

    if (!empty($update['token'])) {
        set_transient('afb_task_pat_' . md5($task_gid), (string) $update['token'], DAY_IN_SECONDS);
    }

    $token_for_story = !empty($update['token']) ? [$update['token']] : $token_candidates;

    // STEP 7: Post story note to Asana for audit trail
    $story_text = "[Feedback] ";
    if ($rating > 0) {
        $story_text .= "Rating: {$rating}/5. ";
    }
    if ($reply !== '') {
        $story_text .= "Reply: {$reply} ";
    }
    if ($feedback !== '') {
        $story_text .= "Feedback: {$feedback}";
    }

    $story = afb_feedback_request_for_task(
        $task_gid,
        "https://app.asana.com/api/1.0/tasks/{$task_gid}/stories",
        'POST',
        ['text' => trim($story_text)],
        $token_for_story,
        [200, 201],
        $task_token
    );

    if (!$story['success']) {
        afb_debug_log("⚠️  Story note failed (non-fatal) | task={$task_gid} http={$story['code']}");
    }

    // STEP 8: Expire gateway token after successful feedback
    afb_expire_gateway_token_local($gateway_token, 'feedback_submitted');

    afb_debug_log("✅ Feedback submitted | token={$gateway_token} task={$task_gid}");

    wp_send_json_success([
        'mode' => 'complete',
        'message' => 'Thank you for your feedback. The gateway is now closed.',
    ]);
}
