= 200 && $statusCode < 300; } public static function ticketStatuses(): array { return [ 'pending_response' => 'در انتظار پاسخ', 'under_review' => 'در حال بررسی', 'closed' => 'بسته شده', 'answered' => 'پاسخ داده شده', ]; } public static function resolveTicketStatus(array $ticket): string { $status = $ticket['data']['status'] ?? $ticket['status'] ?? ''; if (is_array($status)) { $status = $status['slug'] ?? $status['name'] ?? ''; } return strtolower(trim((string) $status)); } public static function isTicketClosed(array $ticket): bool { return in_array(self::resolveTicketStatus($ticket), ['closed', 'بسته شده'], true); } public static function resolveTicketSiteAccessStatus(array $ticket): string { $siteAccess = $ticket['data']['site_access'] ?? $ticket['site_access'] ?? null; if (!is_array($siteAccess)) return ''; $status = $siteAccess['status'] ?? ''; if (is_array($status)) { $status = $status['slug'] ?? $status['name'] ?? ''; } return strtolower(trim((string) $status)); } public static function isTicketSiteAccessRejected(array $ticket): bool { return in_array(self::resolveTicketSiteAccessStatus($ticket), ['invalid', 'rejected'], true); } public function __construct() { $settings = (array) syncBasalamSettings()->getSettings(); $this->hamsalamToken = $settings[SettingsConfig::HAMSALAM_TOKEN] ?? null; $this->hamsalamBusinessId = $settings[SettingsConfig::HAMSALAM_BUSINESS_ID] ?? null; $this->tokenFetcher = new FetchHamsalamToken(); $this->businessIdFetcher = new FetchHamsalamBusinessId(); } private function executeWithRetry(callable $callback, array $callbackArgs = []): array { $attempt = 0; while ($attempt < self::MAX_RETRY_ATTEMPTS) { $attempt++; try { $token = $this->getHamsalamToken(); if (!$this->hasValue($token)) return $this->buildErrorResponse('توکن همسلام در دسترس نیست.', 401); $data = call_user_func_array($callback, array_merge([$token], $callbackArgs)); } catch (RetryableException $e) { return $this->buildErrorResponse($e->getMessage(), $e->getCode() ?: 400); } catch (NonRetryableException $e) { return $this->buildErrorResponse($e->getMessage(), $e->getCode() ?: 400); } catch (\Exception $e) { return $this->buildErrorResponse($e->getMessage(), 500); } if (!is_array($data) || !isset($data['status_code'])) { return $this->buildErrorResponse('پاسخ دریافتی از سرویس نامعتبر است.', 500); } if (!self::isUnauthorized($data)) return $data; if ($attempt >= self::MAX_RETRY_ATTEMPTS) return $data; $this->refreshHamsalamToken(); } return $this->buildErrorResponse('خطای ناشناخته در پردازش درخواست.', 500); } private function buildErrorResponse(string $message, int $statusCode = 500): array { return [ 'status_code' => $statusCode, 'error' => true, 'message' => $message, ]; } private function getHamsalamToken() { if ($this->hasValue($this->hamsalamToken)) return $this->hamsalamToken; $this->hamsalamToken = $this->tokenFetcher->fetch(); return $this->hamsalamToken; } private function refreshHamsalamToken(): void { $this->hamsalamToken = $this->tokenFetcher->fetch(); } private function getHamsalamBusinessId() { if ($this->hasValue($this->hamsalamBusinessId)) return $this->hamsalamBusinessId; $this->hamsalamBusinessId = $this->businessIdFetcher->fetch(); return $this->hamsalamBusinessId; } private function hasValue($value): bool { return !($value === null || $value === ''); } private function isValidTicketPayload($title, $subject, $content): bool { if (!isset($title, $subject, $content)) return false; $title = trim((string) $title); $content = trim((string) $content); return mb_strlen($title) >= 3 && mb_strlen($title) <= 255 && mb_strlen($content) >= 10; } public function CheckHamsalamAccess($page = 1): array { return $this->fetchAllTickets($page); } public function fetchTicketSubjects(): array { $service = new FetchTicketSubjects(); return $this->executeWithRetry([$service, 'execute']); } public function fetchAllTickets($page = 1): array { $service = new FetchAllTickets(); $page = max(1, intval($page)); return $this->executeWithRetry([$service, 'execute'], [$page]); } public function fetchTicket($ticket_id): array { $service = new FetchTicket($ticket_id); return $this->executeWithRetry([$service, 'execute']); } public function uploadTicketMedia($filePath): array { $service = new UploadTicketMedia(); return $this->executeWithRetry([$service, 'execute'], [$filePath]); } public function createTicket($title, $subject, $content, $fileIds = []): array { if (!$this->isValidTicketPayload($title, $subject, $content)) { return $this->buildErrorResponse('اطلاعات وارد شده معتبر نیست', 400); } $service = new CreateTicket(); $data = [ 'title' => $title, 'subject' => $subject, 'content' => $content, 'file_ids' => is_array($fileIds) ? $fileIds : [], 'business_id' => $this->getHamsalamBusinessId(), ]; return $this->executeWithRetry([$service, 'execute'], [$data]); } public function createTicketItem($ticket_id, $content, $fileIds = []): array { $service = new CreateTicketItem($ticket_id); $data = [ 'type' => 'content', 'content' => $content, 'file_ids' => is_array($fileIds) ? $fileIds : [], ]; return $this->executeWithRetry([$service, 'execute'], [$data]); } public function upsertTicketSiteAccess($ticketId, array $accessData): array { $businessId = intval($this->getHamsalamBusinessId()); $ticketId = intval($ticketId); if ($businessId <= 0 || $ticketId <= 0 || empty($accessData)) { return $this->buildErrorResponse('اطلاعات دسترسی معتبر نیست.', 400); } $accessData['ticket_id'] = $ticketId; $service = new UpsertTicketSiteAccess($businessId); return $this->executeWithRetry([$service, 'execute'], [$accessData]); } }