<?php
/**
 * Regenerates /cache/tasks-summary-{token}.json
 * (Used by Subtasks & Timeline tabs)
 *
 * Improvements:
 * - Detect deleted/inaccessible Asana tasks (403/404/410)
 * - Invalidate the gateway token mapping
 * - Persist a short-lived reason flag so the dashboard can show a tailored message
 */

require_once dirname(__DIR__, 4) . '/wp-load.php';
require_once dirname(__DIR__) . '/task-comms/functions.php';

defined('ABSPATH') || exit;

nocache_headers();
header('Content-Type: text/plain; charset=utf-8');

$token = $_GET['token']
    ?? $_GET['asana_comms_token']
    ?? get_query_var('asana_comms_token')
    ?? null;

if (!$token) {
    status_header(400);
    echo 'Missing token';
    exit;
}

$task_gid = afb_get_task_id_by_token($token);
if (!$task_gid) {
    status_header(410);
    echo 'Token expired (no task mapping)';
    exit;
}

$cache_dir  = plugin_dir_path(__DIR__) . 'cache/';
$cache_file = $cache_dir . "tasks-summary-{$token}.json";

if (!is_dir($cache_dir)) {
    @mkdir($cache_dir, 0755, true);
}

/**
 * Invalidate this gateway token so dashboard becomes "Expired"
 * Also store a reason so the expired screen can tailor the message.
 */
function afb__invalidate_gateway_token(string $token, string $cache_file, string $reason): void
{
    // Keep reason around briefly so the UI can explain what happened
    set_transient("afb_token_reason_{$token}", $reason, DAY_IN_SECONDS);

    // Remove the token -> task mapping
    delete_transient("afb_token_task_{$token}");

    // Remove cache file if it exists
    if (is_file($cache_file)) {
        @unlink($cache_file);
    }
}

/**
 * Safe Asana GET wrapper
 */
function afb__asana_get(string $url, string $asana_token, int $timeout = 20): array
{
    $rsp = wp_remote_get($url, [
        'headers' => ['Authorization' => "Bearer {$asana_token}"],
        'timeout' => $timeout,
    ]);

    if (is_wp_error($rsp)) {
        return [
            'ok'   => false,
            'code' => 0,
            'body' => null,
            'err'  => $rsp->get_error_message(),
        ];
    }

    $code = (int) wp_remote_retrieve_response_code($rsp);
    $raw  = (string) wp_remote_retrieve_body($rsp);

    $json = null;
    if ($raw !== '') {
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            $json = $decoded;
        }
    }

    return [
        'ok'   => ($code >= 200 && $code < 300),
        'code' => $code,
        'body' => $json,
        'err'  => null,
    ];
}

/**
 * Build a list of PAT candidates.
 *
 * Order matters:
 * 1) Global option token
 * 2) Stored account PATs (decrypted when possible)
 */
function afb__pat_candidates(): array
{
    $candidates = [];

    $global = (string) get_option('asana_access_token');
    if ($global !== '') {
        $candidates[] = $global;
    }

    global $wpdb;
    $rows = $wpdb->get_results("SELECT pat FROM {$wpdb->prefix}asana_accounts", ARRAY_A);
    if (is_array($rows)) {
        foreach ($rows as $row) {
            $raw = (string)($row['pat'] ?? '');
            if ($raw === '') {
                continue;
            }

            $candidate = function_exists('afb_decrypt') ? (string) afb_decrypt($raw) : $raw;
            if ($candidate !== '') {
                $candidates[] = $candidate;
            }
        }
    }

    return array_values(array_unique($candidates));
}

/**
 * Find a PAT that can read this task.
 */
function afb__resolve_readable_pat_for_task(string $task_gid): array
{
    $saw404 = false;
    $saw410 = false;
    $last_code = 0;

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

        if (is_wp_error($probe)) {
            continue;
        }

        $code = (int) wp_remote_retrieve_response_code($probe);
        $last_code = $code;

        if ($code >= 200 && $code < 300) {
            return [
                'ok' => true,
                'pat' => $pat,
                'code' => $code,
                'saw404' => $saw404,
                'saw410' => $saw410,
            ];
        }

        if ($code === 404) {
            $saw404 = true;
        } elseif ($code === 410) {
            $saw410 = true;
        }
    }

    return [
        'ok' => false,
        'pat' => '',
        'code' => $last_code,
        'saw404' => $saw404,
        'saw410' => $saw410,
    ];
}

$pat_probe = afb__resolve_readable_pat_for_task((string) $task_gid);

if (!$pat_probe['ok']) {
    // Only hard-invalidate when we have explicit not-found signals.
    if (!empty($pat_probe['saw404']) || !empty($pat_probe['saw410'])) {
        afb__invalidate_gateway_token($token, $cache_file, 'deleted');
        status_header(410);
        echo 'Task deleted or inaccessible; gateway invalidated';
        exit;
    }

    status_header(502);
    echo 'Unable to access task with any configured Asana PAT';
    exit;
}

$asana_token = (string) $pat_probe['pat'];

/* ─── 1) Parent task ─────────────────────────────────────────────── */
$parent_fields = implode(',', [
    'name,notes,completed,start_on,due_on,created_at,modified_at',
    'assignee.gid,assignee.name,assignee.email,assignee.photo.image_128x128,assignee.photo.image_60x60,assignee.photo.image_36x36,assignee.photo.image_27x27,assignee.photo.image_21x21',
    'projects.name',
    'followers.gid,followers.name,followers.email,followers.photo.image_21x21',
    'custom_fields.gid',
    'custom_fields.name',
    'custom_fields.type',
    'custom_fields.text_value',
    'custom_fields.number_value',
    'custom_fields.enum_value.gid',
    'custom_fields.enum_value.name',
    'custom_fields.enum_value.color',
    'custom_fields.multi_enum_values.gid',
    'custom_fields.multi_enum_values.name',
    'custom_fields.multi_enum_values.color',
    'memberships.section.name,memberships.project.name',
]);

$parent_url = add_query_arg(
    'opt_fields',
    $parent_fields,
    "https://app.asana.com/api/1.0/tasks/{$task_gid}"
);

$parent_rsp = afb__asana_get($parent_url, $asana_token, 20);

// Deleted task: invalidate gateway and return 410
if (!$parent_rsp['ok'] && in_array($parent_rsp['code'], [404, 410], true)) {
    afb__invalidate_gateway_token($token, $cache_file, 'deleted');
    status_header(410);
    echo 'Task deleted or inaccessible; gateway invalidated';
    exit;
}

if (!$parent_rsp['ok']) {
    status_header(502);
    echo 'Asana API error (parent task). HTTP ' . $parent_rsp['code'];
    exit;
}

$parent = $parent_rsp['body']['data'] ?? [];
if (!is_array($parent) || empty($parent)) {
    status_header(502);
    echo 'Asana API returned empty parent payload';
    exit;
}

// Keep gateway active after completion so clients can submit feedback.

/* ─── 2) Subtasks (paginated, single-pass fields) ────────────────── */
$subs = [];
$sub_fields = urlencode('gid,name,completed,start_on,due_on,resource_subtype,assignee.name,assignee.photo.image_21x21');
$url = "https://app.asana.com/api/1.0/tasks/{$task_gid}/subtasks?opt_fields={$sub_fields}&limit=100";

while ($url) {
    $rsp = afb__asana_get($url, $asana_token, 20);

    if (!$rsp['ok'] && in_array($rsp['code'], [404, 410], true)) {
        afb__invalidate_gateway_token($token, $cache_file, 'deleted');
        status_header(410);
        echo 'Task deleted or inaccessible; gateway invalidated';
        exit;
    }

    if (!$rsp['ok']) {
        status_header(502);
        echo 'Asana API error (subtasks). HTTP ' . $rsp['code'];
        exit;
    }

    $body = $rsp['body'] ?? [];
    foreach (($body['data'] ?? []) as $row) {
        $subs[] = [
            'gid'              => (string) ($row['gid'] ?? ''),
            'name'             => $row['name'] ?? '(No name)',
            'completed'        => (bool) ($row['completed'] ?? false),
            'start_on'         => $row['start_on'] ?? null,
            'due_on'           => $row['due_on'] ?? null,
            'resource_subtype' => $row['resource_subtype'] ?? 'default_task',
            'assignee'         => [
                'name'  => $row['assignee']['name'] ?? 'Unassigned',
                'photo' => $row['assignee']['photo']['image_21x21'] ?? null,
            ],
        ];
    }

    $url = $body['next_page']['uri'] ?? null;
}

/* ─── 4) Write atomic JSON (tmp → rename) ────────────────────────── */
$tmp = $cache_file . '.tmp';

$payload = [
    'parent'   => $parent,
    'subtasks' => $subs,
];

$written = @file_put_contents($tmp, wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
if ($written === false) {
    status_header(500);
    echo 'Failed to write cache temp file';
    exit;
}

if (!@rename($tmp, $cache_file)) {
    @unlink($tmp);
    status_header(500);
    echo 'Failed to finalize cache file';
    exit;
}

status_header(200);
echo 'Regenerated cache for token ' . $token . ' (' . count($subs) . ' subtasks)';
exit;
