<?php
declare(strict_types=1);

require __DIR__ . '/common.php';

$method = $_SERVER['REQUEST_METHOD'];
$env = gobao_load_env();
$requestUser = gobao_current_user(gobao_auth_required($env));
$GLOBALS['GOBAO_REQUEST_USER_ID'] = $requestUser
    ? gobao_safe_session_id((string)$requestUser['user_id'])
    : 'main';

function gobao_task_user(array $input = []): string {
    return (string)($GLOBALS['GOBAO_REQUEST_USER_ID'] ?? 'main');
}

function gobao_task_decode(?string $json, $fallback) {
    $value = json_decode((string)($json ?? ''), true);
    return $value === null ? $fallback : $value;
}

function gobao_task_default_steps(string $goal): array {
    return [
        ['title' => 'Clarify objective and success criteria', 'status' => 'pending'],
        ['title' => 'Gather context, files, tools, and permissions', 'status' => 'pending'],
        ['title' => 'Execute the smallest safe change', 'status' => 'pending'],
        ['title' => 'Verify result, errors, retries, and safety', 'status' => 'pending'],
        ['title' => 'Report completed and remaining work', 'status' => 'pending'],
    ];
}

function gobao_task_step_rows(int $taskId): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    $stmt = $db->prepare('SELECT * FROM agent_task_steps WHERE task_id = :task_id ORDER BY step_index ASC, id ASC');
    $stmt->execute([':task_id' => $taskId]);
    return array_map(static function (array $row): array {
        return [
            'id' => (int)$row['id'],
            'taskId' => (int)$row['task_id'],
            'stepIndex' => (int)$row['step_index'],
            'title' => $row['title'],
            'status' => $row['status'],
            'toolName' => $row['tool_name'],
            'input' => gobao_task_decode($row['input_json'] ?? '{}', []),
            'output' => gobao_task_decode($row['output_json'] ?? '{}', []),
            'error' => $row['error'],
            'startedAt' => $row['started_at'],
            'finishedAt' => $row['finished_at'],
            'updatedAt' => $row['updated_at'],
        ];
    }, $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []);
}

function gobao_task_from_row(array $row, bool $withSteps = true): array {
    $task = [
        'id' => (int)$row['id'],
        'userId' => $row['user_id'],
        'sessionId' => $row['session_id'],
        'title' => $row['title'],
        'goal' => $row['goal'],
        'status' => $row['status'],
        'priority' => (int)$row['priority'],
        'riskLevel' => $row['risk_level'],
        'source' => $row['source'],
        'plan' => gobao_task_decode($row['plan_json'] ?? '[]', []),
        'evidence' => gobao_task_decode($row['evidence_json'] ?? '[]', []),
        'metrics' => gobao_task_decode($row['metrics_json'] ?? '{}', []),
        'verification' => gobao_task_decode($row['verification_json'] ?? '{}', []),
        'rollback' => gobao_task_decode($row['rollback_json'] ?? '{}', []),
        'createdAt' => $row['created_at'],
        'updatedAt' => $row['updated_at'],
        'completedAt' => $row['completed_at'],
        'canceledAt' => $row['canceled_at'],
    ];
    if ($withSteps) {
        $task['steps'] = gobao_task_step_rows((int)$row['id']);
    }
    return $task;
}

function gobao_task_get(int $taskId): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    $stmt = $db->prepare('SELECT * FROM agent_tasks WHERE id = :id');
    $stmt->execute([':id' => $taskId]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$row) {
        gobao_fail('Task not found', 404);
    }
    $requestUserId = (string)($GLOBALS['GOBAO_REQUEST_USER_ID'] ?? '');
    if ($requestUserId !== '' && (string)$row['user_id'] !== $requestUserId) {
        gobao_fail('Task not found', 404);
    }
    return gobao_task_from_row($row, true);
}

function gobao_task_list(string $userId, int $limit = 30, string $status = ''): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    if ($status !== '') {
        $stmt = $db->prepare('SELECT * FROM agent_tasks WHERE user_id = :user_id AND status = :status ORDER BY updated_at DESC LIMIT :limit');
        $stmt->bindValue(':status', $status, PDO::PARAM_STR);
    } else {
        $stmt = $db->prepare('SELECT * FROM agent_tasks WHERE user_id = :user_id ORDER BY updated_at DESC LIMIT :limit');
    }
    $stmt->bindValue(':user_id', $userId, PDO::PARAM_STR);
    $stmt->bindValue(':limit', max(1, min(100, $limit)), PDO::PARAM_INT);
    $stmt->execute();
    return array_map(static fn(array $row): array => gobao_task_from_row($row, false), $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []);
}

function gobao_task_create(array $input): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    $userId = gobao_task_user($input);
    $goal = trim((string)($input['goal'] ?? $input['message'] ?? ''));
    if ($goal === '') {
        gobao_fail('goal is required', 400);
    }
    $title = trim((string)($input['title'] ?? ''));
    if ($title === '') {
        $title = mb_substr($goal, 0, 60);
    }
    $steps = $input['steps'] ?? gobao_task_default_steps($goal);
    if (!is_array($steps) || count($steps) === 0) {
        $steps = gobao_task_default_steps($goal);
    }
    $now = gmdate('c');
    $db->beginTransaction();
    try {
        $stmt = $db->prepare('
            INSERT INTO agent_tasks(user_id, session_id, title, goal, status, priority, risk_level, source, plan_json, evidence_json, metrics_json, verification_json, rollback_json, created_at, updated_at)
            VALUES(:user_id, :session_id, :title, :goal, :status, :priority, :risk_level, :source, :plan_json, :evidence_json, :metrics_json, :verification_json, :rollback_json, :created_at, :updated_at)
        ');
        $stmt->execute([
            ':user_id' => $userId,
            ':session_id' => trim((string)($input['sessionId'] ?? $userId)),
            ':title' => $title,
            ':goal' => $goal,
            ':status' => trim((string)($input['status'] ?? 'planned')) ?: 'planned',
            ':priority' => max(1, min(5, (int)($input['priority'] ?? 3))),
            ':risk_level' => trim((string)($input['riskLevel'] ?? 'normal')) ?: 'normal',
            ':source' => trim((string)($input['source'] ?? 'chat')) ?: 'chat',
            ':plan_json' => gobao_json_encode($steps),
            ':evidence_json' => gobao_json_encode(is_array($input['evidence'] ?? null) ? $input['evidence'] : []),
            ':metrics_json' => gobao_json_encode(is_array($input['metrics'] ?? null) ? $input['metrics'] : []),
            ':verification_json' => '{}',
            ':rollback_json' => gobao_json_encode(['notes' => []]),
            ':created_at' => $now,
            ':updated_at' => $now,
        ]);
        $taskId = (int)$db->lastInsertId();
        $stepInsert = $db->prepare('
            INSERT INTO agent_task_steps(task_id, step_index, title, status, input_json, output_json, created_at, updated_at)
            VALUES(:task_id, :step_index, :title, :status, :input_json, :output_json, :created_at, :updated_at)
        ');
        foreach (array_values($steps) as $i => $step) {
            $step = is_array($step) ? $step : ['title' => (string)$step];
            $stepInsert->execute([
                ':task_id' => $taskId,
                ':step_index' => $i + 1,
                ':title' => trim((string)($step['title'] ?? ('Step ' . ($i + 1)))) ?: ('Step ' . ($i + 1)),
                ':status' => trim((string)($step['status'] ?? 'pending')) ?: 'pending',
                ':input_json' => gobao_json_encode(is_array($step['input'] ?? null) ? $step['input'] : []),
                ':output_json' => '{}',
                ':created_at' => $now,
                ':updated_at' => $now,
            ]);
        }
        $db->commit();
    } catch (Throwable $e) {
        if ($db->inTransaction()) {
            $db->rollBack();
        }
        gobao_fail('Create task failed: ' . $e->getMessage(), 500);
    }
    gobao_audit_record('agent_task_created', [
        'sessionId' => $userId,
        'actor' => trim((string)($input['actor'] ?? 'task_brain')),
        'status' => 'planned',
        'refType' => 'agent_task',
        'refId' => (string)$taskId,
        'title' => $title,
        'payload' => ['goal' => $goal],
    ]);
    return gobao_task_get($taskId);
}

function gobao_task_update_step(array $input): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    $taskId = (int)($input['taskId'] ?? $input['id'] ?? 0);
    if ($taskId <= 0) {
        gobao_fail('taskId is required', 400);
    }
    $stepId = (int)($input['stepId'] ?? 0);
    $stepIndex = (int)($input['stepIndex'] ?? 0);
    if ($stepId <= 0 && $stepIndex <= 0) {
        gobao_fail('stepId or stepIndex is required', 400);
    }
    $status = trim((string)($input['status'] ?? ''));
    if ($status === '') {
        gobao_fail('status is required', 400);
    }
    $now = gmdate('c');
    $where = $stepId > 0 ? 'id = :step_id AND task_id = :task_id' : 'step_index = :step_index AND task_id = :task_id';
    $stmt = $db->prepare("
        UPDATE agent_task_steps
        SET status = :status,
            tool_name = COALESCE(:tool_name, tool_name),
            input_json = :input_json,
            output_json = :output_json,
            error = :error,
            started_at = CASE WHEN :status_in_progress = 1 AND started_at IS NULL THEN :now ELSE started_at END,
            finished_at = CASE WHEN :status_finished = 1 THEN :now ELSE finished_at END,
            updated_at = :now
        WHERE {$where}
    ");
    $stmt->bindValue(':task_id', $taskId, PDO::PARAM_INT);
    if ($stepId > 0) {
        $stmt->bindValue(':step_id', $stepId, PDO::PARAM_INT);
    } else {
        $stmt->bindValue(':step_index', $stepIndex, PDO::PARAM_INT);
    }
    $stmt->bindValue(':status', $status, PDO::PARAM_STR);
    $stmt->bindValue(':tool_name', isset($input['toolName']) ? (string)$input['toolName'] : null, PDO::PARAM_STR);
    $stmt->bindValue(':input_json', gobao_json_encode(is_array($input['input'] ?? null) ? $input['input'] : []), PDO::PARAM_STR);
    $stmt->bindValue(':output_json', gobao_json_encode(is_array($input['output'] ?? null) ? $input['output'] : []), PDO::PARAM_STR);
    $stmt->bindValue(':error', isset($input['error']) ? (string)$input['error'] : null, PDO::PARAM_STR);
    $stmt->bindValue(':status_in_progress', $status === 'in_progress' ? 1 : 0, PDO::PARAM_INT);
    $stmt->bindValue(':status_finished', in_array($status, ['completed', 'failed', 'skipped'], true) ? 1 : 0, PDO::PARAM_INT);
    $stmt->bindValue(':now', $now, PDO::PARAM_STR);
    $stmt->execute();
    $db->prepare('UPDATE agent_tasks SET status = :status, updated_at = :updated_at WHERE id = :id')
       ->execute([':status' => 'in_progress', ':updated_at' => $now, ':id' => $taskId]);
    return gobao_task_get($taskId);
}

function gobao_task_verify(array $input): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    $taskId = (int)($input['taskId'] ?? $input['id'] ?? 0);
    if ($taskId <= 0) {
        gobao_fail('taskId is required', 400);
    }
    $passed = (bool)($input['passed'] ?? false);
    $now = gmdate('c');
    $verification = [
        'passed' => $passed,
        'summary' => trim((string)($input['summary'] ?? '')),
        'checks' => is_array($input['checks'] ?? null) ? $input['checks'] : [],
        'safety' => is_array($input['safety'] ?? null) ? $input['safety'] : [],
        'verifiedAt' => $now,
    ];
    $status = $passed ? (empty($input['complete']) ? 'verified' : 'completed') : 'needs_work';
    $db->prepare('
        UPDATE agent_tasks
        SET status = :status, verification_json = :verification_json, updated_at = :updated_at, completed_at = :completed_at
        WHERE id = :id
    ')->execute([
        ':status' => $status,
        ':verification_json' => gobao_json_encode($verification),
        ':updated_at' => $now,
        ':completed_at' => $status === 'completed' ? $now : null,
        ':id' => $taskId,
    ]);
    return gobao_task_get($taskId);
}

function gobao_task_cancel_or_delete(array $input, string $mode): array {
    $db = gobao_db();
    if (!$db) {
        gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
    }
    $taskId = (int)($input['taskId'] ?? $input['id'] ?? 0);
    if ($taskId <= 0) {
        gobao_fail('taskId is required', 400);
    }
    if ($mode === 'delete') {
        $task = gobao_task_get($taskId);
        $db->prepare('DELETE FROM agent_tasks WHERE id = :id')->execute([':id' => $taskId]);
        return ['deleted' => true, 'task' => $task];
    }
    $now = gmdate('c');
    $task = gobao_task_get($taskId);
    $rollback = $task['rollback'] ?: ['notes' => []];
    $rollback['notes'][] = [
        'type' => 'cancel',
        'reason' => trim((string)($input['reason'] ?? 'Canceled by user')),
        'at' => $now,
    ];
    $db->prepare('UPDATE agent_tasks SET status = :status, rollback_json = :rollback_json, updated_at = :updated_at, canceled_at = :canceled_at WHERE id = :id')
       ->execute([
           ':status' => 'canceled',
           ':rollback_json' => gobao_json_encode($rollback),
           ':updated_at' => $now,
           ':canceled_at' => $now,
           ':id' => $taskId,
       ]);
    return gobao_task_get($taskId);
}

if ($method === 'GET') {
    $action = trim((string)($_GET['action'] ?? 'list'));
    $userId = gobao_task_user();
    if ($action === 'list') {
        gobao_ok(['tasks' => gobao_task_list($userId, (int)($_GET['limit'] ?? 30), trim((string)($_GET['status'] ?? ''))), 'database' => gobao_db_counts()]);
    }
    if ($action === 'view') {
        gobao_ok(['task' => gobao_task_get((int)($_GET['id'] ?? $_GET['taskId'] ?? 0))]);
    }
    gobao_fail('Unsupported GET action', 400);
}

if ($method !== 'POST') {
    gobao_fail('POST required', 405);
}

$input = gobao_json_input();
$action = trim((string)($input['action'] ?? 'create'));

if ($action === 'create') {
    gobao_ok(['task' => gobao_task_create($input)]);
}
if ($action === 'step') {
    gobao_ok(['task' => gobao_task_update_step($input)]);
}
if ($action === 'verify') {
    gobao_ok(['task' => gobao_task_verify($input)]);
}
if ($action === 'cancel') {
    gobao_ok(['task' => gobao_task_cancel_or_delete($input, 'cancel')]);
}
if ($action === 'delete') {
    gobao_ok(gobao_task_cancel_or_delete($input, 'delete'));
}
if ($action === 'list') {
    $userId = gobao_task_user($input);
    gobao_ok(['tasks' => gobao_task_list($userId, (int)($input['limit'] ?? 30), trim((string)($input['status'] ?? '')))]);
}

gobao_fail('Unsupported action', 400);
