<?php
declare(strict_types=1);

namespace App\Modules\Notifications\Services;

use App\Core\App;
use App\Core\Logger;
use App\Modules\Notifications\Models\NotificationTrigger;
use App\Modules\Notifications\Models\ScheduledNotification;
use App\Modules\PlayerApi\Services\PlayerNotificationService;

final class NotificationTriggerService
{
    /**
     * Process an event and create scheduled notifications for all matching active triggers.
     */
    public static function processEvent(string $eventName, array $data): int
    {
        $triggers = NotificationTrigger::getActiveByEvent($eventName);
        if (empty($triggers)) {
            return 0;
        }

        $queued = 0;

        foreach ($triggers as $trigger) {
            try {
                $channel = $trigger['channel'] ?? 'sms';

                if ($channel === 'player_push') {
                    $queued += self::processPlayerPushChannel($trigger, $data, $eventName);
                    continue;
                }

                $recipients = self::resolveRecipients($trigger['recipient_type'], $data);
                if (empty($recipients)) {
                    Logger::debug("No recipients found for trigger {$trigger['trigger_code']} on event {$eventName}");
                    continue;
                }

                $templateId = $trigger['template_id'] ? (int) $trigger['template_id'] : null;
                $message = $templateId ? self::renderMessage($templateId, $data) : null;

                if ($message === null || $message === '') {
                    Logger::warning("Empty message for trigger {$trigger['trigger_code']}, skipping");
                    continue;
                }

                $config = $trigger['config_json'] ? json_decode($trigger['config_json'], true) : [];
                $delay = $config['delay_minutes'] ?? 0;
                $scheduledAt = $delay > 0
                    ? date('Y-m-d H:i:s', strtotime("+{$delay} minutes"))
                    : date('Y-m-d H:i:s');

                foreach ($recipients as $recipient) {
                    if (empty($recipient['phone'])) {
                        continue;
                    }

                    // Deduplicate: skip if already scheduled today for same recipient
                    $alreadyExists = ScheduledNotification::existsForRecipient(
                        $trigger['trigger_code'],
                        $recipient['type'],
                        (int) $recipient['id'],
                        date('Y-m-d')
                    );
                    if ($alreadyExists) {
                        continue;
                    }

                    $contextData = array_merge($data, [
                        'recipient_type' => $recipient['type'],
                        'recipient_id'   => $recipient['id'],
                    ]);

                    $db = App::getInstance()->db();
                    $db->insert('scheduled_notifications', [
                        'trigger_code'    => $trigger['trigger_code'],
                        'recipient_type'  => $recipient['type'],
                        'recipient_id'    => (int) $recipient['id'],
                        'recipient_phone' => $recipient['phone'],
                        'message'         => $message,
                        'scheduled_at'    => $scheduledAt,
                        'status'          => 'pending',
                        'attempts'        => 0,
                        'max_attempts'    => (int) ($config['max_attempts'] ?? 3),
                        'context_json'    => json_encode($contextData, JSON_UNESCAPED_UNICODE),
                        'created_at'      => date('Y-m-d H:i:s'),
                        'updated_at'      => date('Y-m-d H:i:s'),
                    ]);
                    $queued++;
                }
            } catch (\Throwable $e) {
                Logger::error("NotificationTriggerService error for trigger {$trigger['trigger_code']}: {$e->getMessage()}");
            }
        }

        if ($queued > 0) {
            Logger::info("NotificationTriggerService: queued {$queued} notifications for event {$eventName}");
        }

        return $queued;
    }

    /**
     * Handle player_push channel — writes directly to player_notifications table.
     */
    private static function processPlayerPushChannel(array $trigger, array $data, string $eventName): int
    {
        $playerId = (int) ($data['player_id'] ?? 0);
        if ($playerId <= 0) {
            Logger::debug("player_push: no player_id for event {$eventName}");
            return 0;
        }

        $templateId = $trigger['template_id'] ? (int) $trigger['template_id'] : null;
        $message = $templateId ? self::renderMessage($templateId, $data) : null;

        $categoryMap = [
            'player.enrolled' => 'sport',
            'player.booking_confirmed' => 'booking',
            'player.booking_cancelled' => 'booking',
            'player.free_time_entry' => 'booking',
            'player.transfer_result' => 'sport',
            'player.evaluation_completed' => 'rating',
            'player.medical_expiring' => 'medical',
            'facility.attendance_absent' => 'attendance',
        ];

        $category = $categoryMap[$eventName] ?? 'event';
        $title = $data['notification_title'] ?? ($trigger['trigger_name_ar'] ?? 'إشعار');
        $body = $message ?? ($data['notification_body'] ?? '');

        if (empty($body)) {
            return 0;
        }

        PlayerNotificationService::notify($playerId, $category, $title, $body, $data);
        return 1;
    }

    /**
     * Resolve recipients based on recipient type and event data context.
     * Returns array of ['type' => string, 'id' => int, 'phone' => string].
     */
    public static function resolveRecipients(string $recipientType, array $data): array
    {
        $db = App::getInstance()->db();
        $recipients = [];

        switch ($recipientType) {
            case 'player':
                $recipients = self::resolvePlayerRecipient($db, $data);
                break;

            case 'member':
                $recipients = self::resolveMemberRecipient($db, $data);
                break;

            case 'coach':
                $recipients = self::resolveCoachRecipient($db, $data);
                break;

            case 'parent':
                $recipients = self::resolveParentRecipient($db, $data);
                break;

            case 'player_and_parent':
                $playerRecipients = self::resolvePlayerRecipient($db, $data);
                $parentRecipients = self::resolveParentRecipient($db, $data);
                $recipients = array_merge($playerRecipients, $parentRecipients);
                break;

            case 'booker':
                $recipients = self::resolveBookerRecipient($db, $data);
                break;

            case 'group_players':
                $recipients = self::resolveGroupPlayersRecipient($db, $data);
                break;

            case 'session_players':
                $recipients = self::resolveSessionPlayersRecipient($db, $data);
                break;

            case 'direct':
                // Phone provided directly in event data
                if (!empty($data['phone'])) {
                    $recipients[] = [
                        'type'  => 'direct',
                        'id'    => (int) ($data['recipient_id'] ?? $data['player_id'] ?? $data['member_id'] ?? 0),
                        'phone' => $data['phone'],
                    ];
                }
                break;

            default:
                Logger::warning("Unknown recipient type: {$recipientType}");
        }

        return $recipients;
    }

    /**
     * Render a message from a template ID by substituting placeholders.
     */
    public static function renderMessage(int $templateId, array $data): ?string
    {
        $db = App::getInstance()->db();
        $template = $db->selectOne("SELECT * FROM sms_templates WHERE id = ? AND is_active = 1", [$templateId]);

        if (!$template) {
            return null;
        }

        $message = $template['message_template_ar'];

        // Substitute all {placeholder} variables
        foreach ($data as $key => $value) {
            if (is_scalar($value) || $value === null) {
                $message = str_replace('{' . $key . '}', (string) ($value ?? ''), $message);
            }
        }

        // Remove any unresolved placeholders
        $message = preg_replace('/\{[a-zA-Z_]+\}/', '', $message);

        return trim($message);
    }

    // ─── Private Recipient Resolvers ─────────────────────────────────────────────

    private static function resolvePlayerRecipient($db, array $data): array
    {
        $recipients = [];
        $playerId = (int) ($data['player_id'] ?? 0);

        if ($playerId > 0) {
            $player = $db->selectOne(
                "SELECT id, phone FROM players WHERE id = ? AND is_archived = 0",
                [$playerId]
            );
            if ($player && !empty($player['phone'])) {
                $recipients[] = [
                    'type'  => 'player',
                    'id'    => (int) $player['id'],
                    'phone' => $player['phone'],
                ];
            }
        }

        // If phone provided directly (e.g., from cron jobs)
        if (empty($recipients) && !empty($data['phone']) && $playerId > 0) {
            $recipients[] = [
                'type'  => 'player',
                'id'    => $playerId,
                'phone' => $data['phone'],
            ];
        }

        return $recipients;
    }

    private static function resolveMemberRecipient($db, array $data): array
    {
        $recipients = [];
        $memberId = (int) ($data['member_id'] ?? 0);

        if ($memberId > 0) {
            $member = $db->selectOne(
                "SELECT id, phone_mobile FROM members WHERE id = ? AND is_archived = 0",
                [$memberId]
            );
            if ($member && !empty($member['phone_mobile'])) {
                $recipients[] = [
                    'type'  => 'member',
                    'id'    => (int) $member['id'],
                    'phone' => $member['phone_mobile'],
                ];
            }
        }

        return $recipients;
    }

    private static function resolveCoachRecipient($db, array $data): array
    {
        $recipients = [];
        $coachId = (int) ($data['coach_id'] ?? 0);

        if ($coachId > 0) {
            $coach = $db->selectOne(
                "SELECT id, phone FROM coaches WHERE id = ? AND is_archived = 0",
                [$coachId]
            );
            if ($coach && !empty($coach['phone'])) {
                $recipients[] = [
                    'type'  => 'coach',
                    'id'    => (int) $coach['id'],
                    'phone' => $coach['phone'],
                ];
            }
        }

        return $recipients;
    }

    private static function resolveParentRecipient($db, array $data): array
    {
        $recipients = [];
        $playerId = (int) ($data['player_id'] ?? 0);

        if ($playerId > 0) {
            // Find parent phone via player's guardian info
            $player = $db->selectOne(
                "SELECT id, guardian_phone FROM players WHERE id = ? AND is_archived = 0",
                [$playerId]
            );
            if ($player && !empty($player['guardian_phone'])) {
                $recipients[] = [
                    'type'  => 'parent',
                    'id'    => $playerId,
                    'phone' => $player['guardian_phone'],
                ];
            }
        }

        return $recipients;
    }

    private static function resolveBookerRecipient($db, array $data): array
    {
        $recipients = [];
        $phone = $data['booker_phone'] ?? $data['phone'] ?? '';

        if (!empty($phone)) {
            $recipients[] = [
                'type'  => 'booker',
                'id'    => (int) ($data['reservation_id'] ?? $data['member_id'] ?? 0),
                'phone' => $phone,
            ];
        }

        return $recipients;
    }

    private static function resolveGroupPlayersRecipient($db, array $data): array
    {
        $recipients = [];
        $groupId = (int) ($data['group_id'] ?? $data['new_group_id'] ?? 0);

        if ($groupId > 0) {
            $players = $db->select(
                "SELECT p.id, p.phone
                 FROM players p
                 JOIN group_players gp ON gp.player_id = p.id
                 WHERE gp.group_id = ? AND gp.status = 'active'
                   AND p.is_archived = 0 AND p.phone IS NOT NULL AND p.phone != ''",
                [$groupId]
            );
            foreach ($players as $player) {
                $recipients[] = [
                    'type'  => 'player',
                    'id'    => (int) $player['id'],
                    'phone' => $player['phone'],
                ];
            }
        }

        return $recipients;
    }

    private static function resolveSessionPlayersRecipient($db, array $data): array
    {
        $recipients = [];
        $sessionId = (int) ($data['session_id'] ?? 0);

        if ($sessionId > 0) {
            // Get group from session then resolve group players
            $session = $db->selectOne("SELECT group_id FROM sessions WHERE id = ?", [$sessionId]);
            if ($session && $session['group_id']) {
                $data['group_id'] = (int) $session['group_id'];
                return self::resolveGroupPlayersRecipient($db, $data);
            }
        }

        return $recipients;
    }
}
