<?php
namespace App\Services;

use App\Repositories\ActivityRepository;
use App\Repositories\LookupRepository;
use App\Repositories\PersonRepository;
use App\Repositories\ConfigRepository;
use App\Services\DidarApiService;
use App\Services\VirtualStageService;
use App\Services\NotificationService;
use App\Utils\Sanitizer;
use App\Utils\Logger;

/**
 * Activity Service
 */
class ActivityService
{
    private $activityRepo;
    private $personRepo;
    private $didarApi;
    private $virtualStageService;
    private $notificationService;
    private ConfigRepository $configRepo;
    private ?LookupRepository $lookupRepo = null;

    public function __construct(
        ActivityRepository $activityRepo,
        PersonRepository $personRepo,
        DidarApiService $didarApi,
        ConfigRepository $configRepo,
        VirtualStageService $virtualStageService,
        ?NotificationService $notificationService = null
    ) {
        $this->activityRepo = $activityRepo;
        $this->personRepo = $personRepo;
        $this->didarApi = $didarApi;
        $this->configRepo = $configRepo;
        $this->virtualStageService = $virtualStageService;
        $this->notificationService = $notificationService;
    }

    public function getActivitiesByContact($contactId, $limit = 50)
    {
        return $this->activityRepo->findByContactId($contactId, $limit);
    }

    public function getActivitiesByDeal($dealId, $limit = 100)
    {
        return $this->activityRepo->findByDealId($dealId, $limit);
    }

    public function getActivityByDidarId(string $activityId)
    {
        return $this->activityRepo->findByDidarId($activityId);
    }

    public function getActivityById($id)
    {
        return $this->activityRepo->findById($id);
    }

    private function isLocalContactId(?string $contactId): bool
    {
        return !empty($contactId) && strpos($contactId, 'local_') === 0;
    }

    private function isDidarWriteEnabled(): bool
    {
        return false;
    }

    private function getLookupRepo(): LookupRepository
    {
        if ($this->lookupRepo === null) {
            $this->lookupRepo = new LookupRepository($this->personRepo->getDb());
        }
        return $this->lookupRepo;
    }

    private function formatUserDisplayName($user): string
    {
        if (!$user) {
            return '';
        }

        foreach (['display_name', 'name', 'username', 'email'] as $field) {
            $value = trim((string)($user->$field ?? ''));
            if ($value !== '') {
                return $value;
            }
        }

        return trim((string)(($user->first_name ?? '') . ' ' . ($user->last_name ?? '')));
    }

    private function getActivityTypeTitleFromLookup(string $activityTypeId): string
    {
        $activityTypeId = trim($activityTypeId);
        if ($activityTypeId === '') {
            return '';
        }

        try {
            foreach ($this->getLookupRepo()->getItemsByGroupCode('activity_types_fallback') as $item) {
                $value = trim((string)($item['value'] ?? ''));
                if ($value !== '' && strcasecmp($value, $activityTypeId) === 0) {
                    return trim((string)($item['title'] ?? ''));
                }
            }
        } catch (\Throwable $e) {
            Logger::logWarning('Failed to resolve activity type title from lookup', [
                'activity_type_id' => $activityTypeId,
                'error' => $e->getMessage()
            ]);
        }

        return '';
    }

    private function extractActivityTypeTitleFromRawJson(?string $rawJson): string
    {
        $rawJson = trim((string)$rawJson);
        if ($rawJson === '') {
            return '';
        }

        $decoded = json_decode($rawJson, true);
        if (!is_array($decoded)) {
            return '';
        }

        $title = trim((string)($decoded['ActivityTypeTitle'] ?? ''));
        if ($title !== '') {
            return $title;
        }
        $title = trim((string)($decoded['title'] ?? ''));
        if ($title !== '' && $title !== 'فعالیت') {
            return $title;
        }
        $title = trim((string)($decoded['title_alt'] ?? ''));
        if ($title !== '' && $title !== 'فعالیت') {
            return $title;
        }

        $activityNode = $decoded['Activity'] ?? null;
        if (is_array($activityNode)) {
            $activityTitle = trim((string)($activityNode['ActivityTypeTitle'] ?? ''));
            if ($activityTitle !== '') {
                return $activityTitle;
            }
        }

        return '';
    }

    private function resolveActivityTypeTitleFromStage(array $data, ?string $rawJson = null): string
    {
        $stageCandidates = [
            trim((string)($data['stage'] ?? '')),
            trim((string)($data['pipeline_stage'] ?? '')),
        ];
        $decoded = json_decode(trim((string)$rawJson), true);
        if (is_array($decoded)) {
            $stageCandidates[] = trim((string)($decoded['stage'] ?? ''));
            $stageCandidates[] = trim((string)($decoded['ActivityStageTitle'] ?? ''));
            if (isset($decoded['Activity']) && is_array($decoded['Activity'])) {
                $stageCandidates[] = trim((string)($decoded['Activity']['Stage'] ?? ''));
                $stageCandidates[] = trim((string)($decoded['Activity']['ActivityStageTitle'] ?? ''));
            }
        }

        foreach ($stageCandidates as $candidate) {
            if ($candidate === '') {
                continue;
            }
            $normalized = $this->normalizeActivityTypeLabel($candidate);
            if ($normalized !== '' && $normalized !== 'نامشخص') {
                return $normalized;
            }
        }
        return '';
    }

    private function normalizeActivityTypeLabel(string $value): string
    {
        $value = trim($value);
        $code = strtolower(str_replace([' ', '-', '/'], '_', $value));
        if ($value === '' || $value === 'فعالیت') {
            return 'نامشخص';
        }
        if (in_array($code, ['contact_no_answer', 'no_answer', 'contact_not_answered'], true)) {
            return 'تماس عدم پاسخ';
        }
        if (in_array($code, ['contact_followup', 'followup', 'contact_new', 'new_contact'], true)) {
            return 'تماس پیگیری';
        }
        if (in_array($code, ['payment_link', 'send_payment_link'], true)) {
            return 'ارسال لینک پرداخت';
        }
        if (in_array($code, ['outstanding_followup', 'outstanding_balance_followup', 'payment_followup'], true)) {
            return 'پیگیری مانده حساب';
        }
        if (in_array($code, ['payment', 'paid', 'successful_payment'], true)) {
            return 'پرداخت';
        }
        if (in_array($code, ['course_delivered'], true)) {
            return 'برگزاری دوره';
        }
        if (in_array($code, ['without_purchase'], true)) {
            return 'ناموفق';
        }
        if (mb_stripos($value, 'عدم پاسخ') !== false || mb_stripos($value, 'بی پاسخ') !== false) {
            return 'تماس عدم پاسخ';
        }
        if (mb_stripos($value, 'پیگیری مانده') !== false) {
            return 'پیگیری مانده حساب';
        }
        if (mb_stripos($value, 'ارسال لینک') !== false) {
            return 'ارسال لینک پرداخت';
        }
        if (mb_stripos($value, 'برگزاری') !== false || mb_stripos($value, 'دوره') !== false) {
            return 'برگزاری دوره';
        }
        if (mb_stripos($value, 'پشتیبانی') !== false) {
            return 'پشتیبانی';
        }
        if (mb_stripos($value, 'ارجاع') !== false || mb_stripos($value, 'crm') !== false) {
            return 'ارجاع به CRM';
        }
        if (mb_stripos($value, 'ناموفق') !== false) {
            return 'ناموفق';
        }
        if (mb_stripos($value, 'پرداخت') !== false || mb_stripos($value, 'واریز') !== false || mb_stripos($value, 'موفق') !== false) {
            return 'پرداخت';
        }
        if (mb_stripos($value, 'پیگیری') !== false || mb_stripos($value, 'تماس') !== false || mb_stripos($value, 'خروجی') !== false || mb_stripos($value, 'جدید') !== false) {
            return 'تماس پیگیری';
        }
        return $value;
    }

    private function resolveActivityTypeTitle(array $data, ?string $rawJson = null, string $fallback = ''): string
    {
        $candidates = [
            trim((string)($data['activity_type_title'] ?? '')),
            trim((string)$fallback),
            $this->extractActivityTypeTitleFromRawJson($rawJson),
            $this->getActivityTypeTitleFromLookup((string)($data['activity_type_id'] ?? '')),
            $this->resolveActivityTypeTitleFromStage($data, $rawJson),
        ];

        foreach ($candidates as $candidate) {
            if ($candidate !== '' && $candidate !== 'فعالیت') {
                return $this->normalizeActivityTypeLabel($candidate);
            }
        }

        return 'نامشخص';
    }

    /**
     * Returns true if there is at least one local activity for the given deal ID.
     */
    public function hasActivityForDeal(string $dealId): bool
    {
        return $this->activityRepo->hasActivityForDeal($dealId);
    }

    public function saveActivity($data, $virtualStage = null, $currentStage = null, $pipelineStage = null)
    {
        $didarId = $_SESSION['didar_id'] ?? '';
        $contactId = $data['contact_didar_id'] ?? '';
        $isLocalLead = $this->isLocalContactId($contactId);
        $activityTypeId = $data['activity_type_id'] ?? '';
        $note = $data['note'] ?? '';
	        $resultNote = $data['result_note'] ?? '';
	        $activityDuration = max(0, (int)($data['activity_duration'] ?? 0));
	        $failureReasonCode = $data['failure_reason'] ?? null;
	        $failureReasonDesc = $data['failure_other_description'] ?? null;
        $dealId = $data['deal_didar_id'] ?? '';
        $requestedServices = $data['requested_services'] ?? '';
        $city = $data['city'] ?? null;
        $jobTitle = $data['job_title'] ?? null;
        $jobDescription = $data['job_description'] ?? null;
        $acquaintanceDuration = $data['acquaintance_duration'] ?? null;
        $extraInfo = $data['extra_info'] ?? null;
        if (empty(trim((string)$activityTypeId))) {
            throw new \Exception('نوع فعالیت الزامی است');
        }
        if ($activityDuration <= 0) {
            throw new \Exception('مدت زمان فعالیت الزامی است');
        }
        
        // Check if activity is scheduled for future
        $dueDate = $data['due_date'] ?? null;
        $isScheduled = isset($data['is_scheduled']) && $data['is_scheduled'] == '1';
        $reminderEnabled = isset($data['reminder_enabled']) && $data['reminder_enabled'] == '1';
        $reminderOffset = isset($data['reminder_offset_minutes']) ? (int)$data['reminder_offset_minutes'] : 1440; // Default: 24 hours
        
        // If not explicitly set, check if due date is in the future
        if (!$isScheduled && !empty($dueDate)) {
            $dueDateTime = strtotime($dueDate);
            $now = time();
            if ($dueDateTime > $now) {
                $isScheduled = true;
            }
        }
        
        // For scheduled activities, is_done must be 0
        $isDone = $isScheduled ? 0 : ($data['is_done'] ?? 0);
        
        Logger::logInfo("Activity is_done determination", [
            'is_scheduled' => $isScheduled,
            'is_done_from_data' => $data['is_done'] ?? 0,
            'final_is_done' => $isDone
        ]);
        
        // targetStage no longer used for leads; pipelineStage controls deal stage
        
        // Prepare activity data for Didar API
        // Format due date for Didar API (ISO 8601 with Z)
        $formattedDueDate = null;
        if (!empty($dueDate)) {
            try {
                // Parse the date string - format: YYYY-MM-DD HH:mm:ss
                // Try different formats
                $dateTime = null;
                
                // Try YYYY-MM-DD HH:mm:ss format
                if (preg_match('/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})$/', $dueDate, $matches)) {
                    $dateTime = \DateTime::createFromFormat(
                        'Y-m-d H:i:s',
                        $dueDate,
                        new \DateTimeZone('Asia/Tehran')
                    );
                }
                // Try YYYY-MM-DDTHH:mm:ss format
                elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/', $dueDate, $matches)) {
                    $dateTime = \DateTime::createFromFormat(
                        'Y-m-d\TH:i:s',
                        $dueDate,
                        new \DateTimeZone('Asia/Tehran')
                    );
                }
                // Try YYYY-MM-DD HH:mm format
                elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})$/', $dueDate, $matches)) {
                    $dateTime = \DateTime::createFromFormat(
                        'Y-m-d H:i',
                        $dueDate,
                        new \DateTimeZone('Asia/Tehran')
                    );
                }
                // Fallback: use strtotime
                else {
                    $timestamp = strtotime($dueDate);
                    if ($timestamp !== false) {
                        $dateTime = new \DateTime('@' . $timestamp);
                        $dateTime->setTimezone(new \DateTimeZone('Asia/Tehran'));
                    }
                }
                
                if (!$dateTime) {
                    throw new \Exception("Could not parse date: " . $dueDate);
                }
                
                // Convert to UTC for Didar API (Z means UTC)
                $dateTime->setTimezone(new \DateTimeZone('UTC'));
                $formattedDueDate = $dateTime->format('Y-m-d\TH:i:s.000\Z');
                
                Logger::logInfo("Formatted due date for Didar", [
                    'original' => $dueDate,
                    'formatted' => $formattedDueDate
                ]);
            } catch (\Exception $e) {
                Logger::logError("Error formatting due date", $e, ['due_date' => $dueDate]);
                // Final fallback: try strtotime
                try {
                    $timestamp = strtotime($dueDate);
                    if ($timestamp !== false) {
                        $formattedDueDate = gmdate('Y-m-d\TH:i:s.000\Z', $timestamp);
                    } else {
                        throw new \Exception("Could not parse date: " . $dueDate);
                    }
                } catch (\Exception $e2) {
                    Logger::logError("Fallback date formatting also failed", $e2);
                    throw new \Exception("خطا در فرمت تاریخ: " . $dueDate . " - " . $e2->getMessage());
                }
            }
        }
        
        // Get the correct Didar User ID (OwnerId)
        // OwnerId must be the Didar UserId of the current user, not the API key owner
        // $_SESSION['didar_id'] is set from $user->didar_user_id during login (AuthService.php:53)
        // But we need to use the synced didar_user_id from database to ensure it's correct
        $didarUserId = $didarId;
        
        // Get user from database to use the synced didar_user_id
        // Try to find by didar_id first, then by email if not found
        $userRepo = new \App\Repositories\UserRepository($this->personRepo->getDb());
        $currentUser = $userRepo->findByDidarId($didarId);
        
        // If not found by didar_id, try to find by email from session
        if (!$currentUser && !empty($_SESSION['email'])) {
            $currentUser = $userRepo->findByEmail($_SESSION['email']);
        }
        
        if ($currentUser && $currentUser->didar_user_id) {
            // Use the synced didar_user_id from database (this is the correct one from Didar)
            $didarUserId = $currentUser->didar_user_id;
            Logger::logInfo("Using synced didar_user_id from database as OwnerId", [
                'session_didar_id' => $didarId,
                'database_didar_user_id' => $didarUserId,
                'user_email' => $currentUser->email ?? 'unknown',
                'user_name' => ($currentUser->first_name ?? '') . ' ' . ($currentUser->last_name ?? ''),
                'ids_match' => ($didarId === $didarUserId) ? 'yes' : 'no',
                'found_by' => $currentUser->didar_user_id === $didarId ? 'didar_id' : 'email'
            ]);
        } else {
            // User not found or didar_user_id not set - use session didar_id as fallback
            Logger::logWarning("User not found or didar_user_id not set in database. Using session didar_id as OwnerId. Please sync users from Didar first!", [
                'didar_id' => $didarId,
                'session_email' => $_SESSION['email'] ?? 'not_set',
                'user_found' => $currentUser ? 'yes' : 'no',
                'has_didar_user_id' => ($currentUser && $currentUser->didar_user_id) ? 'yes' : 'no'
            ]);
        }

        $activityOwnerNameSnapshot = $this->formatUserDisplayName($currentUser);
        if ($activityOwnerNameSnapshot === '') {
            $activityOwnerNameSnapshot = trim((string)($_SESSION['display_name'] ?? $_SESSION['name'] ?? $_SESSION['username'] ?? ''));
        }
        if ($activityOwnerNameSnapshot === '') {
            $activityOwnerNameSnapshot = (string)$didarUserId;
        }
        
        // Get contact owner name to add to Note
        $leadOwnerName = '';
        try {
            $contact = $this->personRepo->findByDidarId($contactId);
            if ($contact && $contact->owner_didar_id) {
                $ownerUser = $userRepo->findByDidarId($contact->owner_didar_id);
                if ($ownerUser) {
                    $leadOwnerName = trim(($ownerUser->first_name ?? '') . ' ' . ($ownerUser->last_name ?? ''));
                    if (empty($leadOwnerName) && isset($ownerUser->display_name)) {
                        $leadOwnerName = $ownerUser->display_name;
                    }
                }
            }
        } catch (\Exception $e) {
            Logger::logWarning("Could not get lead owner name for activity note", [
                'contact_id' => $contactId,
                'error' => $e->getMessage()
            ]);
        }
        
        // Add lead owner name to Note if available
        $finalNote = $note;
        if (!empty($leadOwnerName)) {
            $ownerInfo = "📌 صاحب لید: " . $leadOwnerName;
            if (!empty($note)) {
                $finalNote = $note . "\n\n" . $ownerInfo;
            } else {
                $finalNote = $ownerInfo;
            }
            Logger::logInfo("Added lead owner name to activity note", [
                'lead_owner_name' => $leadOwnerName,
                'contact_id' => $contactId,
                'note_was_empty' => empty($note)
            ]);
        }
        
        $activityData = [
            'Activity' => [
                'ActivityTypeId' => $activityTypeId,
                'Title' => 'فعالیت',
                'OwnerId' => $didarUserId,
                'CreatorId' => $didarUserId, // شناسه شخص ایجاد کننده فعالیت
                'Note' => $finalNote,
                'ResultNote' => $resultNote,
                'IsDone' => $isDone,
                'ContactIds' => [$contactId],
                'DueDate' => $formattedDueDate ?: date('Y-m-d\TH:i:s.000\Z'),
                'DueDateType' => !empty($formattedDueDate) ? 'Default' : 'NoTime',
                'DoneDateType' => 'Notime'
            ],
            'SetDone' => $isDone
        ];
        
        if (!empty($dealId)) {
            $activityData['Activity']['DealIds'] = [$dealId];
        }

        // Persist requested services on deal locally (no Didar API support)
        if (!empty($dealId) && !empty($requestedServices)) {
            try {
                $dealRepo = new \App\Repositories\DealRepository($this->personRepo->getDb());
                $servicesValue = $requestedServices;
                if (is_array($servicesValue)) {
                    $servicesValue = json_encode(array_values(array_filter($servicesValue)), JSON_UNESCAPED_UNICODE);
                }
                $dealRepo->updateRequestedServices($dealId, $servicesValue);
                // ذخیره در فیلدهای اضافی معامله برای دسترسی بعدی
                $dealRepo->updateExtraFields($dealId, ['requested_services' => $servicesValue]);
            } catch (\Exception $e) {
                Logger::logWarning("Failed to update requested services for deal", [
                    'deal_id' => $dealId,
                    'error' => $e->getMessage()
                ]);
            }
        }

        // Update person/deal extra fields (city, job, etc.)
        if (!empty($contactId)) {
            $pdo = $this->personRepo->getDb()->getPdo();
            $fields = [];
            $params = [];
            $map = [
                'city' => $city,
                'job_title' => $jobTitle,
                'job_description' => $jobDescription,
                'acquaintance_duration' => $acquaintanceDuration,
                'extra_info' => $extraInfo,
                'financial_level' => $data['financial_level'] ?? null,
                'asset_estimation' => $data['asset_estimation'] ?? null,
                'income_estimation' => $data['income_estimation'] ?? null,
                'requested_services' => !empty($requestedServices) ? (is_array($requestedServices) ? json_encode(array_values(array_filter($requestedServices)), JSON_UNESCAPED_UNICODE) : $requestedServices) : null,
                'has_previous_purchase' => ($data['has_previous_purchase'] ?? $data['has_previous_purchase_other_number'] ?? '') !== '' ? (int)($data['has_previous_purchase'] ?? $data['has_previous_purchase_other_number']) : null
            ];
            foreach ($map as $col => $val) {
                if ($val !== null) {
                    $fields[] = "$col = ?";
                    $params[] = $val;
                }
            }
            if (!empty($fields)) {
                $params[] = $contactId;
                try {
                    $stmt = $pdo->prepare("UPDATE persons SET " . implode(', ', $fields) . " WHERE didar_contact_id = ?");
                    $stmt->execute($params);
                    if (!empty($requestedServices)) {
                        $this->personRepo->syncCustomerProductsFromRequestedServicesForContact((string)$contactId);
                    }
                } catch (\Exception $e) {
                    Logger::logWarning("Failed to update person extra fields", ['contact_id' => $contactId, 'error' => $e->getMessage()]);
                }
            }
        }

        if (!empty($dealId)) {
            try {
                $dealRepo = isset($dealRepo) ? $dealRepo : new \App\Repositories\DealRepository($this->personRepo->getDb());
                $dealRepo->updateExtraFields($dealId, [
                    'city' => $city,
                    'job_title' => $jobTitle,
                    'job_description' => $jobDescription,
                    'acquaintance_duration' => $acquaintanceDuration,
                    'extra_info' => $extraInfo,
                    'financial_level' => $data['financial_level'] ?? null,
                    'asset_estimation' => $data['asset_estimation'] ?? null,
                    'income_estimation' => $data['income_estimation'] ?? null,
                    'has_previous_purchase' => ($data['has_previous_purchase'] ?? $data['has_previous_purchase_other_number'] ?? '') !== '' ? (int)($data['has_previous_purchase'] ?? $data['has_previous_purchase_other_number']) : null
                ]);
            } catch (\Exception $e) {
                Logger::logWarning("Failed to update deal extra fields", ['deal_id' => $dealId, 'error' => $e->getMessage()]);
            }
        }
        
        // Add Notifies if reminder is enabled and activity is scheduled
        if ($isScheduled && $reminderEnabled) {
            $notifies = [];
            $notifyOffset = $reminderOffset; // minutes before due date
            $notifies[] = [
                'Id' => '00000000-0000-0000-0000-000000000000',
                'ActivityId' => '00000000-0000-0000-0000-000000000000',
                'Type' => 'Notification',
                'Group' => 'Owner',
                'Data' => null,
                'NotifyOffset' => $notifyOffset,
                'SentDate' => null
            ];
            $activityData['Activity']['Notifies'] = $notifies;
        }
        
        $shouldSyncToDidar = $this->isDidarWriteEnabled() && !$isLocalLead;

        if (!$shouldSyncToDidar) {
            if (!$isLocalLead) {
                Logger::logInfo("Didar write disabled; saving activity locally", [
                    'contact_id' => $contactId,
                    'owner_id' => $didarUserId
                ]);
            }
            $activityId = uniqid('local_activity_', true);
            $activityTypeTitle = $this->resolveActivityTypeTitle($data);
            $res = [
                'Response' => [
                    'Id' => $activityId,
                    'ActivityTypeTitle' => $activityTypeTitle,
                    'Duration' => $activityDuration,
                    'LocalOnly' => true
                ]
            ];
        } else {
            // Call Didar API
            Logger::logInfo("Calling Didar API to save activity", [
                'contact_id' => $contactId,
                'owner_id' => $didarUserId,
                'creator_id' => $didarUserId,
                'activity_data' => [
                    'ActivityTypeId' => $activityTypeId,
                    'OwnerId' => $didarUserId,
                    'CreatorId' => $didarUserId,
                    'IsDone' => $isDone,
                    'IsScheduled' => $isScheduled
                ]
            ]);
            $res = $this->didarApi->call('/activity/save', 'POST', $activityData);
        }
        
        // Log the response to verify OwnerId and CreatorId were accepted
        if (isset($res['Response']['OwnerId']) || isset($res['Response']['Owner']) || isset($res['Response']['CreatorId']) || isset($res['Response']['Creator'])) {
            Logger::logInfo("Activity saved with OwnerId and CreatorId from Didar response", [
                'requested_owner_id' => $didarUserId,
                'requested_creator_id' => $didarUserId,
                'response_owner_id' => $res['Response']['OwnerId'] ?? 'not_set',
                'response_creator_id' => $res['Response']['CreatorId'] ?? 'not_set',
                'owner_name' => $res['Response']['Owner']['DisplayName'] ?? ($res['Response']['OwnerName'] ?? 'unknown'),
                'creator_name' => $res['Response']['Creator']['DisplayName'] ?? ($res['Response']['CreatorName'] ?? 'unknown'),
                'owner_match' => (isset($res['Response']['OwnerId']) && $didarUserId === $res['Response']['OwnerId']) ? 'yes' : 'no',
                'creator_match' => (isset($res['Response']['CreatorId']) && $didarUserId === $res['Response']['CreatorId']) ? 'yes' : 'no'
            ]);
        }
        
        if ($shouldSyncToDidar && (isset($res['error']) || !isset($res['Response']['Id']))) {
            $errorMsg = $res['error'] ?? ($res['message'] ?? 'Unknown error');
            
            // Check for CURL errors (network issues)
            if (isset($res['curl_error']) || (isset($res['curl_errno']) && $res['curl_errno'] > 0)) {
                $curlError = $res['curl_error'] ?? 'خطا در اتصال به سرور دیدار';
                Logger::logError("Network error connecting to Didar API", null, [
                    'error' => $curlError,
                    'curl_errno' => $res['curl_errno'] ?? null,
                    'response' => $res
                ]);
                throw new \Exception("خطا در اتصال به سرور دیدار. لطفاً اتصال اینترنت و DNS سرور را بررسی کنید: " . $curlError);
            }
            
            Logger::logError("Failed to save activity to Didar", null, [
                'error' => $errorMsg,
                'response' => $res
            ]);
            throw new \Exception("خطا در ثبت فعالیت در دیدار: " . $errorMsg);
        }
        
        $activityId = $res['Response']['Id'];
        
        // Update deal pipeline stage / status if provided
        if (!empty($pipelineStage) && !empty($dealId)) {
            try {
                $dealRepo = new \App\Repositories\DealRepository($this->personRepo->getDb());
                $personRepo = $this->personRepo;
                $deal = $dealRepo->findByDidarId($dealId);
                $statusChange = null;
                $stageNormalized = $pipelineStage;
                $isSuccessStage = in_array($pipelineStage, ['deal_success', 'اتمام', 'موفق', 'معامله موفق', 'پرداخت']);
                $isFailedStage = in_array($pipelineStage, ['deal_failed', 'معامله ناموفق', 'ناموفق']);

                if ($isSuccessStage) {
                    $requiredAmount = $deal && property_exists($deal, 'payable_amount') && $deal->payable_amount !== null
                        ? max(0.0, floatval($deal->payable_amount))
                        : max(0.0, floatval($deal->service_cost ?? 0));
                    // Ensure payments exist for success
                    $payments = [];
                    if ($deal && !empty($deal->payments)) {
                        $payments = json_decode($deal->payments, true) ?: [];
                    }
                    $hasValidPayment = false;
                    foreach ($payments as $p) {
                        $amount = isset($p['amount']) ? floatval($p['amount']) : 0;
                        $date = trim((string)($p['date'] ?? ''));
                        $datePersian = trim((string)($p['date_persian'] ?? ''));
                        $method = trim((string)($p['method'] ?? ''));
                        if ($amount > 0 && ($date !== '' || $datePersian !== '') && $method !== '') {
                            $hasValidPayment = true;
                            break;
                        }
                    }
                    if ($requiredAmount > 0 && !$hasValidPayment) {
                        Logger::logWarning("Pipeline stage change to success blocked: no payments", [
                            'deal_id' => $dealId,
                            'contact_id' => $contactId
                        ]);
                        throw new \Exception('برای ثبت معامله موفق، پرداخت کامل ثبت نشده است');
                    }
                    $totalPaid = 0.0;
                    foreach ($payments as $p) {
                        $totalPaid += isset($p['amount']) ? floatval($p['amount']) : 0;
                    }
                    if ($requiredAmount > 0 && ($totalPaid + 0.0001) < $requiredAmount) {
                        Logger::logWarning("Pipeline stage change to success blocked: insufficient payments", [
                            'deal_id' => $dealId,
                            'contact_id' => $contactId,
                            'total_paid' => $totalPaid,
                            'required_amount' => $requiredAmount
                        ]);
                        throw new \Exception('برای موفق شدن معامله، مجموع پرداخت‌ها باید حداقل برابر مبلغ قابل پرداخت باشد');
                    }
                    $statusChange = 'Won';
                    $stageNormalized = 'اتمام';
                } elseif ($isFailedStage) {
                    $statusChange = 'Lost';
                    $stageNormalized = 'اتمام';
                }

                if ($statusChange) {
                    $dealRepo->updateStatus($dealId, $statusChange, $statusChange === 'Won' ? 'won_time' : 'lost_time');

                    if ($statusChange === 'Lost' && (!empty($failureReasonCode) || !empty($failureReasonDesc))) {
                        $reasonMap = [
                            'no_financial_ability' => 'عدم توان مالی',
                            'no_purchase_intent' => 'فعلا قصد خرید ندارد',
                            'competitor_purchase' => 'خرید از رقبا',
                            'wrong_number' => 'شماره اشتباه',
                            'irrelevant_lead' => 'لید نامرتبط',
                            'other' => 'سایر',
                        ];
                        $code = !empty($failureReasonCode) ? trim((string)$failureReasonCode) : null;
                        $desc = !empty($failureReasonDesc) ? trim((string)$failureReasonDesc) : null;
                        $text = null;

                        if ($code) {
                            if (isset($reasonMap[$code])) {
                                $text = $reasonMap[$code];
                            } else {
                                // Older clients might send Persian text in failure_reason
                                $text = $code;
                                $code = null;
                            }
                        }

                        $dealRepo->updateFailureReason($dealId, $code, $text, $desc);
                    } elseif ($statusChange === 'Won') {
                        $dealRepo->updateFailureReason($dealId, null, null, null);
                    }
                }

                $dealRepo->updatePipelineStage($dealId, $stageNormalized);

        // Sync lead sale status unless already successful
        if (!empty($contactId)) {
            // NOTE: virtual stage is recomputed once at the end of saveActivity (after all
            // status + pipeline-stage writes below), so it is intentionally NOT recomputed
            // here — avoids running the full stage engine twice for the same contact per save.
            if ($statusChange) {
                $person = $personRepo->findByDidarId($contactId);
                $currentSaleStatus = $person ? (int)$person->sale_status : null;
                $saleStatus = $statusChange === 'Won' ? 1 : 0;
                $shouldUpdate = true;
                if ($saleStatus === 0 && $currentSaleStatus === 1) {
                    $shouldUpdate = false;
                }
                if ($shouldUpdate) {
                    $personRepo->updateSaleStatus($contactId, $saleStatus);
                }
            }
        }

                Logger::logInfo("Pipeline stage updated after activity save", [
                    'deal_id' => $dealId,
                    'stage' => $stageNormalized,
                    'status_change' => $statusChange
                ]);
            } catch (\Exception $e) {
                Logger::logError("Error updating deal pipeline stage after activity", $e, [
                    'deal_id' => $dealId,
                    'stage' => $pipelineStage
                ]);
                throw $e;
            }
        }
        
        // Save to local DB
        $savedDueDate = !empty($dueDate) ? date('Y-m-d H:i:s', strtotime($dueDate)) : date('Y-m-d H:i:s');
        
	        $activity = new \App\Models\Activity([
	            'didar_activity_id' => $activityId,
	            'activity_type_id' => $activityTypeId,
	            'activity_type_title' => $this->resolveActivityTypeTitle($data, null, (string)($res['Response']['ActivityTypeTitle'] ?? '')),
	            'owner_didar_id' => $didarUserId,
	            'activity_owner_didar_id' => $didarUserId,
	            'activity_owner_name_snapshot' => $activityOwnerNameSnapshot,
	            'activity_owner_locked_at' => date('Y-m-d H:i:s'),
	            'deal_didar_id' => $dealId ?: null,
	            'contact_didar_id' => $contactId,
	            'title' => 'فعالیت',
	            'note' => $note,
	            'result_note' => $resultNote,
	            'failure_reason_code' => $failureReasonCode ?: null,
	            'failure_reason_description' => $failureReasonDesc ?: null,
	            'is_done' => $isDone,
	            'due_date' => $savedDueDate,
	            'done_date' => $isDone ? date('Y-m-d H:i:s') : null,
	            'duration' => max(0, (int)($res['Response']['Duration'] ?? $activityDuration)),
            'call_type' => $data['call_type'] ?? null,
            'direction' => $data['direction'] ?? 'outgoing',
            'stage' => $pipelineStage ?? '',
            'is_scheduled' => $isScheduled ? 1 : 0,
            'reminder_enabled' => $reminderEnabled ? 1 : 0,
            'register_date' => date('Y-m-d H:i:s'),
            'last_sync' => date('Y-m-d H:i:s'),
            'raw_json' => json_encode($res['Response'], JSON_UNESCAPED_UNICODE)
        ]);
        
        $localId = $this->activityRepo->save($activity);

        // Assign a customer code to the lead on its first activity (idempotent: only once).
        if (!empty($contactId)) {
            $this->personRepo->assignCustomerCodeIfMissing((string)$contactId);
        }

        // Schedule reminder if enabled
        if ($isScheduled && $reminderEnabled && $this->notificationService) {
            Logger::logInfo("Attempting to schedule activity reminder", [
                'activity_id' => $activityId,
                'is_scheduled' => $isScheduled,
                'reminder_enabled' => $reminderEnabled,
                'saved_due_date' => $savedDueDate,
                'reminder_offset' => $reminderOffset,
                'notification_service_available' => !is_null($this->notificationService)
            ]);
            $reminderResult = $this->notificationService->scheduleActivityReminder($activityId, $savedDueDate, $reminderOffset);
            Logger::logInfo("Reminder scheduling result", [
                'activity_id' => $activityId,
                'result' => $reminderResult
            ]);
        } else {
            Logger::logWarning("Reminder not scheduled", [
                'activity_id' => $activityId,
                'is_scheduled' => $isScheduled,
                'reminder_enabled' => $reminderEnabled,
                'notification_service_available' => !is_null($this->notificationService)
            ]);
        }
        
        // Notify lead owner if activity is for someone else's lead
        if ($this->notificationService && !empty($contactId)) {
            $person = $this->personRepo->findByDidarId($contactId);
            if ($person && $person->owner_didar_id && $person->owner_didar_id !== $didarId) {
                $contactName = trim(($person->first_name ?? '') . ' ' . ($person->last_name ?? ''));
                $this->notificationService->notifyNewActivity(
                    $person->owner_didar_id,
                    $activityId,
                    $res['Response']['ActivityTypeTitle'] ?? 'فعالیت',
                    $contactName ?: 'لید'
                );
            }
        }
        
        Logger::logInfo("Activity saved successfully", ['activity_id' => $activityId]);
        
        // CRITICAL: Trigger virtual stage update after activity save to ensure leads list is accurate
        try {
            if (!empty($contactId)) {
                $this->virtualStageService->update($contactId);
                Logger::logInfo("Virtual stage updated after activity save", ['contact_id' => $contactId]);
            }
        } catch (\Exception $e) {
            Logger::logWarning("Failed to update virtual stage after activity save", [
                'contact_id' => $contactId,
                'error' => $e->getMessage()
            ]);
        }
        
        return $activityId;
    }

    public function updateScheduledActivity($activityId, $data)
    {
        $didarId = $_SESSION['didar_id'] ?? '';
        $activityTypeId = $data['activity_type_id'] ?? '';
        $dueDate = $data['due_date'] ?? null;
        $reminderEnabled = isset($data['reminder_enabled']) && $data['reminder_enabled'] == '1';
        $reminderOffset = isset($data['reminder_offset_minutes']) ? (int)$data['reminder_offset_minutes'] : 1440;
        
        if (empty($activityTypeId)) {
            throw new \Exception('نوع فعالیت الزامی است');
        }
        
        // Get existing activity. Local-only activities have no didar_activity_id, so the
        // frontend sends the numeric primary key — fall back to a lookup by id.
        $existingActivity = $this->activityRepo->findByDidarId($activityId);
        $localRowId = null;
        if (!$existingActivity && is_numeric($activityId)) {
            $existingActivity = $this->activityRepo->findById((int) $activityId);
            if ($existingActivity) {
                $localRowId = (int) $activityId;
            }
        }
        if (!$existingActivity) {
            throw new \Exception('فعالیت یافت نشد');
        }

        // Addressed by primary key (no didar_activity_id): update the row in place so we
        // don't create a duplicate keyed on the numeric id via the upsert path below.
        if ($localRowId !== null) {
            $savedDueDate = !empty($dueDate) ? date('Y-m-d H:i:s', strtotime($dueDate)) : ($existingActivity->due_date ?? date('Y-m-d H:i:s'));
            $this->activityRepo->updateScheduledById($localRowId, [
                'activity_type_id' => $activityTypeId,
                'activity_type_title' => $this->resolveActivityTypeTitle($data, (string)($existingActivity->raw_json ?? ''), (string)($existingActivity->activity_type_title ?? '')),
                'due_date' => $savedDueDate,
                'is_scheduled' => !empty($dueDate) ? 1 : 0,
                'reminder_enabled' => $reminderEnabled ? 1 : 0,
                'last_update_time' => date('Y-m-d H:i:s'),
                'last_sync' => date('Y-m-d H:i:s'),
            ]);

            if (!empty($existingActivity->contact_didar_id)) {
                $this->personRepo->assignCustomerCodeIfMissing((string)$existingActivity->contact_didar_id);
            }

            if ($this->notificationService) {
                // Reminders for local rows without a didar id are keyed by the numeric id.
                $reminderKey = $existingActivity->didar_activity_id ?: (string) $localRowId;
                $this->notificationService->deleteActivityReminders($reminderKey);
                if ($reminderEnabled && !empty($dueDate)) {
                    $this->notificationService->scheduleActivityReminder($reminderKey, $savedDueDate, $reminderOffset);
                }
            }

            Logger::logInfo("Local-by-id activity updated successfully", ['id' => $localRowId]);
            return (string) $localRowId;
        }

        $isLocalLead = $this->isLocalContactId($existingActivity->contact_didar_id ?? '');
        $isLocalActivity = is_string($activityId) && strpos($activityId, 'local_activity_') === 0;
        if ($isLocalLead || $isLocalActivity) {
            $savedDueDate = !empty($dueDate) ? date('Y-m-d H:i:s', strtotime($dueDate)) : ($existingActivity->due_date ?? date('Y-m-d H:i:s'));
            $activity = new \App\Models\Activity([
                'didar_activity_id' => $activityId,
                'activity_type_id' => $activityTypeId,
                'activity_type_title' => $this->resolveActivityTypeTitle($data, (string)($existingActivity->raw_json ?? ''), (string)($existingActivity->activity_type_title ?? '')),
                'owner_didar_id' => $existingActivity->owner_didar_id ?? $didarId,
                'activity_owner_didar_id' => $existingActivity->activity_owner_didar_id ?? ($existingActivity->owner_didar_id ?? $didarId),
                'activity_owner_name_snapshot' => $existingActivity->activity_owner_name_snapshot ?? null,
                'activity_owner_locked_at' => $existingActivity->activity_owner_locked_at ?? null,
                'contact_didar_id' => $existingActivity->contact_didar_id,
                'title' => $existingActivity->title ?? 'فعالیت',
                'note' => $existingActivity->note ?? '',
                'result_note' => $existingActivity->result_note ?? '',
                'is_done' => $existingActivity->is_done ?? 0,
                'due_date' => $savedDueDate,
                'is_scheduled' => !empty($dueDate) ? 1 : 0,
                'reminder_enabled' => $reminderEnabled ? 1 : 0,
                'last_update_time' => date('Y-m-d H:i:s'),
                'last_sync' => date('Y-m-d H:i:s'),
                'raw_json' => json_encode(['local_only' => true, 'updated_at' => date('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE)
            ]);
            $this->activityRepo->save($activity);

            if (!empty($existingActivity->contact_didar_id)) {
                $this->personRepo->assignCustomerCodeIfMissing((string)$existingActivity->contact_didar_id);
            }

            if ($this->notificationService) {
                $this->notificationService->deleteActivityReminders($activityId);
                if ($reminderEnabled && !empty($dueDate)) {
                    $this->notificationService->scheduleActivityReminder($activityId, $savedDueDate, $reminderOffset);
                }
            }

            Logger::logInfo("Local-only activity updated successfully", ['activity_id' => $activityId]);
            return $activityId;
        }
        
        // Format due date for Didar API
        $formattedDueDate = null;
        if (!empty($dueDate)) {
            try {
                $dateString = str_replace(' ', 'T', $dueDate);
                if (strpos($dateString, '+') === false && strpos($dateString, 'Z') === false) {
                    $dateTime = new \DateTime($dateString, new \DateTimeZone('Asia/Tehran'));
                } else {
                    $dateTime = new \DateTime($dateString);
                }
                $dateTime->setTimezone(new \DateTimeZone('UTC'));
                $formattedDueDate = $dateTime->format('Y-m-d\TH:i:s.000\Z');
            } catch (\Exception $e) {
                Logger::logError("Error formatting due date for update", $e, ['due_date' => $dueDate]);
                $timestamp = strtotime($dueDate);
                if ($timestamp !== false) {
                    $formattedDueDate = gmdate('Y-m-d\TH:i:s.000\Z', $timestamp);
                } else {
                    throw new \Exception("خطا در فرمت تاریخ: " . $dueDate);
                }
            }
        }
        
        // Get the correct Didar User ID (OwnerId)
        $didarUserId = $didarId;
        $userRepo = new \App\Repositories\UserRepository($this->personRepo->getDb());
        $currentUser = $userRepo->findByDidarId($didarId);
        
        if ($currentUser && $currentUser->didar_user_id && $currentUser->didar_user_id !== $didarId) {
            $didarUserId = $currentUser->didar_user_id;
        }
        
        // Prepare activity data for Didar API
        $activityData = [
            'Activity' => [
                'Id' => $activityId,
                'ActivityTypeId' => $activityTypeId,
                'Title' => 'فعالیت',
                'OwnerId' => $didarUserId,
                'CreatorId' => $didarUserId, // شناسه شخص ایجاد کننده فعالیت
                'Note' => $existingActivity->note ?? '',
                'ResultNote' => $existingActivity->result_note ?? '',
                'IsDone' => $existingActivity->is_done ?? 0,
                'ContactIds' => [$existingActivity->contact_didar_id],
                'DueDate' => $formattedDueDate ?: date('Y-m-d\TH:i:s.000\Z'),
                'DueDateType' => !empty($formattedDueDate) ? 'Default' : 'NoTime',
                'DoneDateType' => 'Notime'
            ],
            'SetDone' => $existingActivity->is_done ?? 0
        ];
        
        // Add Notifies if reminder is enabled
        if ($reminderEnabled && !empty($formattedDueDate)) {
            $notifies = [];
            $notifies[] = [
                'Id' => '00000000-0000-0000-0000-000000000000',
                'ActivityId' => '00000000-0000-0000-0000-000000000000',
                'Type' => 'Notification',
                'Group' => 'Owner',
                'Data' => null,
                'NotifyOffset' => $reminderOffset,
                'SentDate' => null
            ];
            $activityData['Activity']['Notifies'] = $notifies;
        }
        
        // Call Didar API
        Logger::logInfo("Calling Didar API to update activity", ['activity_id' => $activityId]);
        $res = $this->didarApi->call('/activity/save', 'POST', $activityData);
        
        if (isset($res['error']) || !isset($res['Response']['Id'])) {
            $errorMsg = $res['error'] ?? ($res['message'] ?? 'Unknown error');
            Logger::logError("Failed to update activity in Didar", null, [
                'error' => $errorMsg,
                'response' => $res
            ]);
            throw new \Exception("خطا در به‌روزرسانی فعالیت در دیدار: " . $errorMsg);
        }
        
        // Update local database
        $savedDueDate = !empty($dueDate) ? date('Y-m-d H:i:s', strtotime($dueDate)) : date('Y-m-d H:i:s');
	        $activity = new Activity([
	            'didar_activity_id' => $activityId,
	            'activity_type_id' => $activityTypeId,
	            'activity_type_title' => $this->resolveActivityTypeTitle($data, null, (string)($res['Response']['ActivityTypeTitle'] ?? ($existingActivity->activity_type_title ?? ''))),
	            'owner_didar_id' => $didarId,
	            'contact_didar_id' => $existingActivity->contact_didar_id,
	            'title' => $res['Response']['Title'] ?? 'فعالیت',
	            'note' => $existingActivity->note ?? '',
	            'result_note' => $existingActivity->result_note ?? '',
	            'failure_reason_code' => $existingActivity->failure_reason_code ?? null,
	            'failure_reason_description' => $existingActivity->failure_reason_description ?? null,
	            'is_done' => $existingActivity->is_done ?? 0,
	            'due_date' => $savedDueDate,
	            'is_scheduled' => !empty($dueDate) ? 1 : 0,
	            'reminder_enabled' => $reminderEnabled ? 1 : 0,
	            'last_update_time' => date('Y-m-d H:i:s'),
            'last_sync' => date('Y-m-d H:i:s'),
            'raw_json' => json_encode($res['Response'], JSON_UNESCAPED_UNICODE)
        ]);
        
        $this->activityRepo->save($activity);

        if (!empty($existingActivity->contact_didar_id)) {
            $this->personRepo->assignCustomerCodeIfMissing((string)$existingActivity->contact_didar_id);
        }

        // Update reminder if enabled
        if ($this->notificationService) {
            // Delete old reminders
            $this->notificationService->deleteActivityReminders($activityId);
            
            // Create new reminder if enabled
            if ($reminderEnabled && !empty($dueDate)) {
                $this->notificationService->scheduleActivityReminder($activityId, $savedDueDate, $reminderOffset);
            }
        }
        
        Logger::logInfo("Activity updated successfully", ['activity_id' => $activityId]);
        
        return $activityId;
    }

    public function deleteActivity($activityId)
    {
        return $this->activityRepo->delete($activityId);
    }
}
