<?php
ini_set("display_errors",1);
error_reporting(E_ALL);

define('LAST_JOBS_FILE', 'last_jobs.json');
define('TOKENS_FILE', 'tokens.json');
define('JOBS_CACHE_FILE', 'jobs_cache.json');

/**
 * Authentication library for Upwork API using OAuth 2.0
 * Example: authenticate and send a test request to the protected resource
 *
 * @final
 * @package     UpworkAPI
 * @since       03/02/2018
 * @copyright   Copyright 2018(c) Upwork.com
 * @author      Maksym Novozhylov <mnovozhilov@upwork.com>
 * @license     Upwork's API Terms of Use {@link https://developers.upwork.com/api-tos.html}
 */

require __DIR__ . '/vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

$client_id = "85ccd1109e8d9b4ff2b57a8881e234f1";
$client_secret = "1c5fbf61c3091e52";
$teams_webhook_url = "https://default2a7119d098a64da2a8684573dcc841.5d.environment.api.powerplatform.com:443/powerautomate/automations/direct/workflows/61f2ec8132864b4bad0caf0db40db967/triggers/manual/paths/invoke?api-version=1&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=Y3heB8VCQ4pg8IEqIAhMGRWJUAF5Bx9fx8v_1AN_cCs";
$openai_api_key = "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";

function loadTokens()
{
    if (!file_exists(TOKENS_FILE)) {
        die("ERROR: tokens.json not found.\nPlease run 'php setup.php' first to authorize the application and get initial tokens.\n");
    }
    
    $data = json_decode(file_get_contents(TOKENS_FILE), true);
    if (!$data || !isset($data['access_token']) || !isset($data['refresh_token']) || !isset($data['expires_in'])) {
        die("ERROR: Invalid tokens.json file.\nPlease run 'php setup.php' to get valid tokens.\n");
    }
    
    return $data;
}

function saveTokens($tokenInfo)
{
    file_put_contents(TOKENS_FILE, json_encode($tokenInfo, JSON_PRETTY_PRINT));
    echo "Tokens saved to " . TOKENS_FILE . "\n";
}

$tokens = loadTokens();
$access_token = $tokens['access_token'];
$refresh_token = $tokens['refresh_token'];
$expires_in = $tokens['expires_in'];

$config = new \Upwork\API\Config(
    array(
        'clientId'          => $client_id,
        'clientSecret'      => $client_secret,
        'grantType'         => 'authorization_code',
        'redirectUri'       => 'https://encloudsolutions.com/upwork',
        'accessToken'       => $access_token,
        'refreshToken'      => $refresh_token,
        'expiresIn'         => $expires_in,
        'mode'              => 'web',
        'authType'       => 'OAuth2ClientLib',
    )
);

$client = new \Upwork\API\Client($config);

$accessTokenInfo = $client->auth();
var_dump($accessTokenInfo);

if ($accessTokenInfo['access_token'] !== $access_token || $accessTokenInfo['refresh_token'] !== $refresh_token) {
    echo "Token was refreshed. Saving new tokens...\n";
    saveTokens($accessTokenInfo);
} else {
    echo "Token is still valid. No refresh needed.\n";
}

$graphql = new \Upwork\API\Routers\Graphql($client);
// Define the query
// Define the updated query
$query = <<<QUERY
query GetJobPostings(
  \$marketPlaceJobFilter: MarketplaceJobPostingsSearchFilter,
  \$searchType: MarketplaceJobPostingSearchType,
  \$sortAttributes: [MarketplaceJobPostingSearchSortAttribute]
) {
  marketplaceJobPostingsSearch(
    marketPlaceJobFilter: \$marketPlaceJobFilter,
    searchType: \$searchType,
    sortAttributes: \$sortAttributes
  ) {
    totalCount
    edges {
      node {
        id
        title
        description
        createdDateTime
        experienceLevel
        hourlyBudgetType
        hourlyBudgetMin {
          displayValue
          currency
          rawValue
        }
        hourlyBudgetMax {
          displayValue
          currency
          rawValue
        }
        weeklyBudget {
          displayValue
          currency
          rawValue
        }
        amount {
          displayValue
          currency
          rawValue
        }
        totalApplicants
        client {
          totalHires
          totalPostedJobs
          totalSpent {
            displayValue
            currency
            rawValue
          }
          totalReviews
          totalFeedback
          verificationStatus
          location {
            country
            state
            city
            timezone
          }
        }
        job {
          id
          content {
            title
            description
          }
          classification {
            skills {
              preferredLabel
              ontologyId
            }
            category {
              preferredLabel
              ontologyId
            }
          }
          contractTerms {
            contractType
            experienceLevel
            personsToHire
            fixedPriceContractTerms {
              amount {
                displayValue
                currency
                rawValue
              }
            }
            hourlyContractTerms {
              hourlyBudgetType
              hourlyBudgetMin
              hourlyBudgetMax
            }
          }
          activityStat {
            applicationsBidStats {
              avgRateBid {
                displayValue
                currency
                rawValue
              }
              minRateBid {
                displayValue
                currency
                rawValue
              }
              maxRateBid {
                displayValue
                currency
                rawValue
              }
            }
            jobActivity {
              invitesSent
              totalInvitedToInterview
              totalHired
              totalUnansweredInvites
              totalOffered
              lastClientActivity
            }
          }
          clientCompanyPublic {
            country {
              name
            }
            state
            city
            timezone
            paymentVerification {
              status
              paymentVerified
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
QUERY;

// ✅ Corrected Filter Fields
$variables = [
    'marketPlaceJobFilter' => [
        'searchExpression_eq' => 'zoho', // Adjust based on available fields
        // Include other valid filters as needed
    ],
    'searchType' => 'USER_JOBS_SEARCH',
];

// Set the parameters
$params = [
    'query' => $query,
    'variables' => $variables
];

// Execute the query
$response = $graphql->execute($params);

// // Handle response
// if (isset($response->errors)) {
//     var_dump($response->errors);
// } else {
//     $jobs = $response->data->marketplaceJobPostings->edges;
//     foreach ($jobs as $job) {
//         $jobData = $job->node;

//         // Convert UTC to Pakistan Time (UTC+5)
//         $utcDateTime = new DateTime($jobData->createdDateTime, new DateTimeZone("UTC"));
//         $utcDateTime->setTimezone(new DateTimeZone("Asia/Karachi")); // Convert to PKT

//         echo "Job ID: " . $jobData->id . "<br/>";
//         echo "Title: " . $jobData->title . "<br/>";
//         echo "Description: " . $jobData->description . "<br/>";
//         echo "Skills: " . implode(', ', array_map(fn($s) => $s->name, $jobData->skills)) . "<br/>";
//         echo "Posted At (PKT): " . $utcDateTime->format("Y-m-d H:i:s") . "\n"; // Converted time
//         echo "<br/><br/>";

//     }
// }


// Check for errors
if (isset($response->errors)) {
    die('GraphQL Error: ' . json_encode($response->errors));
}

// Extract jobs
$jobsConnection = $response->data->marketplaceJobPostingsSearch ?? null;
if (!$jobsConnection) {
    die("GraphQL Error: Unexpected response structure.");
}
$jobs = $jobsConnection->edges ?? [];
// echo "<pre>";
// var_dump($response);


// Function to get previously sent job IDs
function getStoredJobIds()
{
    if (file_exists(LAST_JOBS_FILE)) {
        $data = json_decode(file_get_contents(LAST_JOBS_FILE), true);
        return $data['job_ids'] ?? [];
    }
    return [];
}

// Function to store newly sent job IDs
function storeJobIds($jobIds)
{
    file_put_contents(LAST_JOBS_FILE, json_encode(['job_ids' => $jobIds]));
}

function normalizeMoney($money)
{
    if (!$money) {
        return null;
    }

    if (is_object($money)) {
        $display = $money->displayValue ?? null;
        $currency = $money->currency ?? ($money->currencyCode ?? null);
        $raw = $money->rawValue ?? null;

        if (!$display && $raw !== null && $currency) {
            $display = sprintf('%s %s', $currency, number_format((float)$raw, 2));
        }

        return [
            'display' => $display ?? ($raw !== null ? (string)$raw : null),
            'currency' => $currency,
            'raw' => $raw !== null ? (float)$raw : null,
        ];
    }

    if (is_numeric($money)) {
        return [
            'display' => number_format((float)$money, 2),
            'currency' => null,
            'raw' => (float)$money,
        ];
    }

    return null;
}

function formatContractLabel(?string $type, ?string $experienceLevel): ?string
{
    $parts = [];

    if ($type) {
        $parts[] = ucwords(strtolower(str_replace('_', ' ', $type)));
    }

    if ($experienceLevel) {
        $parts[] = ucwords(strtolower(str_replace('_', ' ', $experienceLevel)));
    }

    return $parts ? implode(' • ', $parts) : null;
}

function formatMoneyDisplay(?array $money, string $suffix = ''): ?string
{
    if (!$money) {
        return null;
    }

    $display = $money['display'] ?? null;
    if (!$display && $money['raw'] !== null) {
        $display = number_format($money['raw'], 2);
    }

    if (!$display) {
        return null;
    }

    if ($suffix) {
        $display .= $suffix;
    }

    return $display;
}

function formatBudgetLine(array $contract): ?string
{
    $type = $contract['type'] ?? null;

    if ($type === 'FIXED_PRICE') {
        return formatMoneyDisplay($contract['fixedAmount']);
    }

    if ($type === 'HOURLY') {
        $min = formatMoneyDisplay($contract['hourlyMin'], '/hr');
        $max = formatMoneyDisplay($contract['hourlyMax'], '/hr');

        if ($min && $max && $min !== $max) {
            return $min . ' - ' . $max;
        }

        return $min ?? $max;
    }

    if (!empty($contract['weeklyBudget'])) {
        return formatMoneyDisplay($contract['weeklyBudget'], ' /week');
    }

    return null;
}

function formatDateTimePk(?string $isoString): ?string
{
    if (!$isoString) {
        return null;
    }

    try {
        $utc = new DateTime($isoString, new DateTimeZone('UTC'));
        $utc->setTimezone(new DateTimeZone('Asia/Karachi'));
        return $utc->format('Y-m-d h:i A') . ' PKT';
    } catch (Exception $e) {
        return $isoString;
    }
}

function normalizeJobEdge($edge): ?array
{
    if (!isset($edge->node)) {
        return null;
    }

    $node = $edge->node;
    $job = $node->job ?? new stdClass();
    $content = $job->content ?? new stdClass();

    $title = $content->title ?? $node->title ?? 'Untitled';
    $description = $content->description ?? $node->description ?? '';
    $createdUtc = $job->createdDateTime ?? $node->createdDateTime ?? null;

    // Extract skills with multiple fallback paths
    $skills = [];

    // Primary: job.classification.skills
    if (isset($job->classification->skills) && is_array($job->classification->skills)) {
        foreach ($job->classification->skills as $skill) {
            $label = $skill->preferredLabel ?? $skill->name ?? null;
            if ($label) {
                $skills[] = $label;
            }
        }
    }

    // Fallback 1: node.classification.skills
    if (empty($skills) && isset($node->classification->skills) && is_array($node->classification->skills)) {
        foreach ($node->classification->skills as $skill) {
            $label = $skill->preferredLabel ?? $skill->name ?? null;
            if ($label) {
                $skills[] = $label;
            }
        }
    }

    // Fallback 2: node.skills (direct)
    if (empty($skills) && isset($node->skills) && is_array($node->skills)) {
        foreach ($node->skills as $skill) {
            if (is_object($skill)) {
                $label = $skill->preferredLabel ?? $skill->name ?? null;
                if ($label) {
                    $skills[] = $label;
                }
            } elseif (is_string($skill)) {
                $skills[] = $skill;
            }
        }
    }

    // Clean up and format
    $skills = array_values(array_filter(array_unique($skills)));
    $skills = array_map(fn($name) => ['name' => $name], $skills);

    // Extract category and specialty information
    $category = null;
    $specialty = null;
    $occupations = [];

    // Try job.classification.category
    if (isset($job->classification->category)) {
        $cat = $job->classification->category;
        $category = $cat->preferredLabel ?? $cat->name ?? null;
    }

    // Try job.classification.subcategories (might be an array)
    if (isset($job->classification->subcategories)) {
        if (is_array($job->classification->subcategories) && !empty($job->classification->subcategories)) {
            $spec = $job->classification->subcategories[0];
            $specialty = $spec->preferredLabel ?? $spec->name ?? null;
        } elseif (is_object($job->classification->subcategories)) {
            $specialty = $job->classification->subcategories->preferredLabel ?? $job->classification->subcategories->name ?? null;
        }
    }

    // Try job.classification.occupations (array)
    if (isset($job->classification->occupations) && is_array($job->classification->occupations)) {
        foreach ($job->classification->occupations as $occupation) {
            $occLabel = $occupation->preferredLabel ?? $occupation->name ?? null;
            if ($occLabel) {
                $occupations[] = $occLabel;
            }
        }
    }

    // Fallback: Try node.category directly
    if (!$category && isset($node->category)) {
        $category = is_string($node->category) ? $node->category : ($node->category->preferredLabel ?? null);
    }

    // Limit occupations to avoid clutter (max 3)
    $occupations = array_slice($occupations, 0, 3);

    $contractTerms = $job->contractTerms ?? new stdClass();
    $contractType = $contractTerms->contractType ?? $node->jobType ?? null;
    $experienceLevel = $contractTerms->experienceLevel ?? $node->experienceLevel ?? null;

    $clientSearch = $node->client ?? new stdClass();
    $clientLocation = $clientSearch->location ?? new stdClass();
    $companyPublic = $job->clientCompanyPublic ?? new stdClass();
    $paymentVerification = $companyPublic->paymentVerification ?? new stdClass();

    $country = $companyPublic->country->name ?? $clientLocation->country ?? null;
    $state = $companyPublic->state ?? $clientLocation->state ?? null;
    $city = $companyPublic->city ?? $clientLocation->city ?? null;
    $timezone = $companyPublic->timezone ?? $clientLocation->timezone ?? null;

    $activityStats = $job->activityStat->jobActivity ?? new stdClass();
    $bidStats = $job->activityStat->applicationsBidStats ?? new stdClass();

    $jobId = $job->id ?? $node->id ?? null;
    if (!$jobId) {
        return null;
    }

    return [
        'id' => $jobId,
        'title' => $title,
        'description' => $description,
        'createdDateTimeUtc' => $createdUtc,
        'skills' => $skills,
        'category' => $category,
        'specialty' => $specialty,
        'occupations' => $occupations,
        'contract' => [
            'type' => $contractType,
            'experienceLevel' => $experienceLevel,
            'personsToHire' => $contractTerms->personsToHire ?? $node->freelancersToHire ?? null,
            'fixedAmount' => normalizeMoney($contractTerms->fixedPriceContractTerms->amount ?? $node->amount ?? null),
            'hourlyMin' => normalizeMoney($node->hourlyBudgetMin ?? null),
            'hourlyMax' => normalizeMoney($node->hourlyBudgetMax ?? null),
            'hourlyBudgetType' => $node->hourlyBudgetType ?? $contractTerms->hourlyContractTerms->hourlyBudgetType ?? null,
            'weeklyBudget' => normalizeMoney($node->weeklyBudget ?? null),
        ],
        'client' => [
            'country' => $country,
            'state' => $state,
            'city' => $city,
            'timezone' => $timezone,
            'verificationStatus' => $clientSearch->verificationStatus ?? null,
            'paymentVerified' => $paymentVerification->paymentVerified ?? null,
            'paymentStatus' => $paymentVerification->status ?? null,
            'totalSpent' => normalizeMoney($clientSearch->totalSpent ?? null),
            'totalHires' => $clientSearch->totalHires ?? null,
            'totalPostedJobs' => $clientSearch->totalPostedJobs ?? null,
            'totalFeedback' => $clientSearch->totalFeedback ?? null,
            'totalReviews' => $clientSearch->totalReviews ?? null,
        ],
        'activity' => [
            'avgBid' => normalizeMoney($bidStats->avgRateBid ?? null),
            'minBid' => normalizeMoney($bidStats->minRateBid ?? null),
            'maxBid' => normalizeMoney($bidStats->maxRateBid ?? null),
            'invitesSent' => $activityStats->invitesSent ?? null,
            'totalInvited' => $activityStats->totalInvitedToInterview ?? null,
            'totalHired' => $activityStats->totalHired ?? null,
            'totalUnansweredInvites' => $activityStats->totalUnansweredInvites ?? null,
            'totalOffered' => $activityStats->totalOffered ?? null,
            'lastClientActivity' => $activityStats->lastClientActivity ?? null,
            'totalApplicants' => $node->totalApplicants ?? null,
        ],
        'cached_at' => time(),
    ];
}

function cacheJobDetails(array $jobs): void
{
    if (empty($jobs)) {
        return;
    }

    $cache = [];
    if (file_exists(JOBS_CACHE_FILE)) {
        $existing = json_decode(file_get_contents(JOBS_CACHE_FILE), true);
        if (is_array($existing)) {
            $cache = $existing;
        }
    }

    foreach ($jobs as $job) {
        if (!isset($job['id'])) {
            continue;
        }
        $cache[$job['id']] = $job;
    }

    $oneDayAgo = time() - 86400;
    $cache = array_filter($cache, fn($job) => ($job['cached_at'] ?? 0) > $oneDayAgo);

    file_put_contents(JOBS_CACHE_FILE, json_encode($cache, JSON_PRETTY_PRINT));
}

function sendTeamsNotification(array $jobs): void
{
    global $teams_webhook_url;

    if (empty($jobs)) {
        return;
    }

    //$serverName = $_SERVER['SERVER_NAME'] ?? 'encloudsolutions.com';
    $serverName = "http://3.235.184.10";
    //$serverName = "http://localhost/upwork";

    foreach ($jobs as $job) {
        $skills = array_map(fn($s) => $s['name'], $job['skills'] ?? []);
        $skillsText = 'Not specified';

        if ($skills) {
            $displaySkills = array_slice($skills, 0, 6);
            $moreCount = count($skills) - 6;
            $skillsText = implode(', ', $displaySkills);
            if ($moreCount > 0) {
                $skillsText .= " (+" . $moreCount . " more)";
            }
        }

        $description = trim(preg_replace('/\s+/', ' ', strip_tags($job['description'] ?? '')));
        if (strlen($description) > 650) {
            $description = substr($description, 0, 650) . '...';
        }

        $jobUrl = "https://www.upwork.com/jobs/~02{$job['id']}";
        $draftUrl = "{$serverName}/php-up-oauth2/generate_draft.php?job_id=" . urlencode($job['id']);

        $facts = [];

        $posted = formatDateTimePk($job['createdDateTimeUtc'] ?? null);
        if ($posted) {
            $facts[] = ['title' => 'Posted', 'value' => $posted];
        }

        $contractLabel = formatContractLabel($job['contract']['type'] ?? null, $job['contract']['experienceLevel'] ?? null);
        if ($contractLabel) {
            $facts[] = ['title' => 'Contract', 'value' => $contractLabel];
        }

        $budget = formatBudgetLine($job['contract'] ?? []);
        if ($budget) {
            $facts[] = ['title' => 'Budget', 'value' => $budget];
        }

        if (!empty($job['contract']['personsToHire'])) {
            $facts[] = ['title' => 'Hiring', 'value' => (string)$job['contract']['personsToHire'] . ' freelancer(s)'];
        }

        $locationParts = array_filter([
            $job['client']['city'] ?? null,
            $job['client']['state'] ?? null,
            $job['client']['country'] ?? null,
        ]);
        if ($locationParts) {
            $locationText = implode(', ', $locationParts);
            if (!empty($job['client']['timezone'])) {
                $locationText .= ' (' . $job['client']['timezone'] . ')';
            }
            $facts[] = ['title' => 'Client Location', 'value' => $locationText];
        }

        if (!empty($job['client']['totalSpent'])) {
            $facts[] = ['title' => 'Client Spend', 'value' => formatMoneyDisplay($job['client']['totalSpent'])];
        }

        if (!empty($job['client']['totalHires'])) {
            $facts[] = ['title' => 'Client Hires', 'value' => (string)$job['client']['totalHires']];
        }

        if (!empty($job['client']['totalFeedback'])) {
            $rating = number_format((float)$job['client']['totalFeedback'], 1);
            $reviews = $job['client']['totalReviews'] ?? 0;
            $facts[] = ['title' => 'Client Rating', 'value' => $rating . '/5 (' . $reviews . ' reviews)'];
        }

        $verificationParts = [];
        if (!empty($job['client']['verificationStatus'])) {
            $verificationParts[] = str_replace('_', ' ', ucfirst(strtolower($job['client']['verificationStatus'])));
        }
        if (array_key_exists('paymentVerified', $job['client'])) {
            $verificationParts[] = $job['client']['paymentVerified'] ? 'Payment verified' : 'Payment unverified';
        }
        if ($verificationParts) {
            $facts[] = ['title' => 'Verification', 'value' => implode(' • ', $verificationParts)];
        }

        if (!empty($job['activity']['avgBid'])) {
            $facts[] = ['title' => 'Avg Bid', 'value' => formatMoneyDisplay($job['activity']['avgBid'], '/hr') ?? formatMoneyDisplay($job['activity']['avgBid'])];
        }

        if (!empty($job['activity']['totalApplicants'])) {
            $facts[] = ['title' => 'Applicants', 'value' => (string)$job['activity']['totalApplicants']];
        }

        $lastActivity = formatDateTimePk($job['activity']['lastClientActivity'] ?? null);
        if ($lastActivity) {
            $facts[] = ['title' => 'Last Client Activity', 'value' => $lastActivity];
        }

        $summaryLines = [];

        if (!empty($job['activity']['minBid']) || !empty($job['activity']['maxBid'])) {
            $minBidDisplay = formatMoneyDisplay($job['activity']['minBid'], '/hr') ?? formatMoneyDisplay($job['activity']['minBid']);
            $maxBidDisplay = formatMoneyDisplay($job['activity']['maxBid'], '/hr') ?? formatMoneyDisplay($job['activity']['maxBid']);
            $bidParts = array_filter([$minBidDisplay, $maxBidDisplay]);
            if ($bidParts) {
                $summaryLines[] = "**Bid Range:** " . implode(' - ', $bidParts);
            }
        }

        $messageText = $description ?: 'No description provided.';
        if (!empty($summaryLines)) {
            $messageText .= "\n\n" . implode("\n", $summaryLines);
        }

        $body = [
            [
                "type" => "TextBlock",
                "size" => "Large",
                "weight" => "Bolder",
                "text" => $job['title'],
                "color" => "Accent",
                "wrap" => true
            ]
        ];

        // Build category and specialty badges (not skills)
        $categoryParts = [];
        $skillParts = [];

        // Add category first
        if (!empty($job['category'])) {
            $categoryParts[] = '`' . $job['category'] . '`';
        }

        // Add specialty if exists
        if (!empty($job['specialty'])) {
            $categoryParts[] = '`' . $job['specialty'] . '`';
        }

        // Add occupations if exist
        if (!empty($job['occupations'])) {
            foreach ($job['occupations'] as $occupation) {
                $categoryParts[] = '`' . $occupation . '`';
            }
        }

        // Add skills (limit to 5 to avoid clutter)
        if (!empty($skills)) {
            $skillsToShow = array_slice($skills, 0, 5);
            foreach ($skillsToShow as $skill) {
                $skillParts[] = '`' . $skill . '`';
            }

            // Add indicator if more skills exist
            $remainingSkills = count($skills) - 5;
            if ($remainingSkills > 0) {
                $skillParts[] = '`+' . $remainingSkills . ' more`';
            }
        }

        // Build badge text with separator between category and skills
        if (!empty($categoryParts) || !empty($skillParts)) {
            $badgeText = '';

            if (!empty($categoryParts)) {
                $badgeText .= implode(' ', $categoryParts);
            }

            if (!empty($categoryParts) && !empty($skillParts)) {
                $badgeText .= ' • ';  // Bullet separator between category and skills
            }

            if (!empty($skillParts)) {
                $badgeText .= implode(' ', $skillParts);
            }

            $body[] = [
                "type" => "TextBlock",
                "text" => $badgeText,
                "wrap" => true,
                "spacing" => "Small",
                "color" => "Good",
                "weight" => "Bolder",
                "separator" => false
            ];
        }

        if (!empty($facts)) {
            $body[] = [
                "type" => "FactSet",
                "facts" => array_map(fn($fact) => [
                    "title" => $fact['title'],
                    "value" => $fact['value']
                ], $facts)
            ];
        }

        $body[] = [
            "type" => "TextBlock",
            "text" => $messageText,
            "wrap" => true,
            "separator" => true
        ];

        $adaptiveCard = [
            "type" => "AdaptiveCard",
            "version" => "1.4",
            "body" => $body,
            "actions" => [
                [
                    "type" => "Action.OpenUrl",
                    "title" => "View on Upwork",
                    "url" => $jobUrl
                ],
                [
                    "type" => "Action.OpenUrl",
                    "title" => "Generate Cover Letter",
                    "url" => $draftUrl
                ]
            ]
        ];

        $message = [
            "attachments" => [
                [
                    "contentType" => "application/vnd.microsoft.card.adaptive",
                    "content" => $adaptiveCard
                ]
            ]
        ];

        $ch = curl_init($teams_webhook_url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($message));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode >= 200 && $httpCode < 300) {
            echo "Teams notification sent for job: {$job['title']}\n";
        } else {
            echo "Failed to send Teams notification. HTTP Code: {$httpCode}\n";
            echo "Response: {$response}\n";
        }

        sleep(1);
    }
}

// Get stored job IDs
$storedJobIds = getStoredJobIds();

// Extract current job IDs
$currentJobIds = array_map(function ($edge) {
    $node = $edge->node ?? null;
    if (!$node) {
        return null;
    }
    return $node->job->id ?? $node->id ?? null;
}, $jobs);
$currentJobIds = array_values(array_filter(array_unique($currentJobIds)));
// var_dump($currentJobIds);

// Find new jobs (jobs not in storedJobIds)
$newJobEdges = array_filter($jobs, function ($edge) use ($storedJobIds) {
    $node = $edge->node ?? null;
    if (!$node) {
        return false;
    }
    $jobId = $node->job->id ?? $node->id ?? null;
    return $jobId && !in_array($jobId, $storedJobIds, true);
});

if (!empty($newJobEdges)) {
    $newJobSummaries = array_map('normalizeJobEdge', $newJobEdges);
    $newJobSummaries = array_values(array_filter($newJobSummaries));
    $newJobSummaries = array_reverse($newJobSummaries);

    cacheJobDetails($newJobSummaries);
    sendTeamsNotification($newJobSummaries);
    storeJobIds($currentJobIds);
    echo "Teams notifications sent for " . count($newJobSummaries) . " new job(s).\n";
} else {
    echo "No new jobs found. No notifications sent.\n";
}
?>