<?php
/**
 * Brevo Transactional SMS Service
 *
 * Handles sending SMS notifications via the Brevo API v3.
 * API Docs: https://developers.brevo.com/docs/transactional-sms-endpoints
 *
 * @package AuctionForge
 * @since 3.6.0.18
 */

namespace auctionforge\inc\services;

use auctionforge\inc\Debug;
use auctionforge\inc\helpers\SettingsHelper;
use auctionforge\inc\model\SMSSubscriber;

class BrevoSMS
{
    const API_ENDPOINT = 'https://api.brevo.com/v3/transactionalSMS/sms';

    /** @var int Max SMS per outbid event per user/item combo within cooldown window */
    const OUTBID_COOLDOWN_SECONDS = 300; // 5 minutes

    /** @var int Max SMS length before multi-segment charging */
    const SMS_MAX_LENGTH = 160;

    /**
     * Get the Brevo API key from settings or wp-config constant.
     *
     * @return string
     */
    private static function getApiKey()
    {
        if (defined('AUCTIONFORGE_BREVO_API_KEY')) {
            return AUCTIONFORGE_BREVO_API_KEY;
        }
        return SettingsHelper::getOption('brevo_api_key', '');
    }

    /**
     * Get the SMS sender name (max 11 alphanumeric chars).
     *
     * @return string
     */
    private static function getSenderName()
    {
        $sender = SettingsHelper::getOption('brevo_sms_sender', '');
        if (empty($sender)) {
            // Fallback: derive from blog name, truncate to 11 chars, alphanumeric only
            $sender = preg_replace('/[^A-Za-z0-9]/', '', get_option('blogname', 'Auction'));
            $sender = substr($sender, 0, 11);
        }
        return $sender;
    }

    /**
     * Check if SMS notifications are globally enabled.
     *
     * @return bool
     */
    public static function isEnabled()
    {
        $apiKey = self::getApiKey();
        if (empty($apiKey)) {
            return false;
        }
        return SettingsHelper::getOption('brevo_sms_enabled', 'off') === 'on';
    }

    /**
     * Check if a specific notification type is enabled.
     *
     * @param string $type One of: auction_published, auction_starts_soon, item_live_soon, outbid
     * @return bool
     */
    public static function isNotificationEnabled($type)
    {
        if (!self::isEnabled()) {
            return false;
        }
        return SettingsHelper::getOption('brevo_sms_' . $type, 'on') === 'on';
    }

    /**
     * Send a single SMS via the Brevo API.
     *
     * @param string $recipient Phone number with country code (e.g. "13125551234")
     * @param string $content   SMS message content
     * @param string $tag       Tag for analytics categorization
     * @return array ['success' => bool, 'messageId' => string|null, 'error' => string|null]
     */
    public static function sendSMS($recipient, $content, $tag = '')
    {
        $apiKey = self::getApiKey();
        if (empty($apiKey)) {
            return ['success' => false, 'messageId' => null, 'error' => 'Brevo API key not configured'];
        }

        // Strip any non-numeric characters from recipient (except leading +)
        $recipient = preg_replace('/[^0-9]/', '', $recipient);

        if (empty($recipient) || strlen($recipient) < 8) {
            return ['success' => false, 'messageId' => null, 'error' => 'Invalid phone number: ' . $recipient];
        }

        $payload = [
            'sender'         => self::getSenderName(),
            'recipient'      => $recipient,
            'content'        => $content,
            'type'           => 'transactional',
            'unicodeEnabled' => true,
        ];

        if (!empty($tag)) {
            $payload['tag'] = $tag;
        }

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => self::API_ENDPOINT,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_TIMEOUT        => 10,
            CURLOPT_HTTPHEADER     => [
                'accept: application/json',
                'api-key: ' . $apiKey,
                'content-type: application/json',
            ],
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        curl_close($ch);

        if ($curlError) {
            self::log('error', 'cURL error sending SMS to ' . $recipient . ': ' . $curlError, $tag);
            return ['success' => false, 'messageId' => null, 'error' => 'cURL error: ' . $curlError];
        }

        $body = json_decode($response, true);

        if ($httpCode >= 200 && $httpCode < 300) {
            $messageId = $body['messageId'] ?? null;
            self::log('info', 'SMS sent to ' . $recipient . ' (tag: ' . $tag . ', msgId: ' . $messageId . ')', $tag);
            return ['success' => true, 'messageId' => $messageId, 'error' => null];
        } else {
            $errorMsg = $body['message'] ?? ($body['code'] ?? 'HTTP ' . $httpCode);
            self::log('error', 'SMS failed to ' . $recipient . ': ' . $errorMsg, $tag);
            return ['success' => false, 'messageId' => null, 'error' => $errorMsg];
        }
    }

    /**
     * Send SMS to multiple recipients for a broadcast event (e.g. auction_published).
     *
     * @param string $notificationType The notification type for opt-in filtering
     * @param string $content          SMS message content
     * @param string $tag              Tag for analytics
     * @return array ['sent' => int, 'failed' => int, 'skipped' => int]
     */
    public static function sendBroadcast($notificationType, $content, $tag = '')
    {
        $stats = ['sent' => 0, 'failed' => 0, 'skipped' => 0];

        if (!self::isNotificationEnabled($notificationType)) {
            return $stats;
        }

        $subscribers = SMSSubscriber::getOptedInPhones($notificationType);
        if (empty($subscribers)) {
            return $stats;
        }

        foreach ($subscribers as $sub) {
            $phone = $sub->country_code . $sub->phone_number;
            $result = self::sendSMS($phone, $content, $tag);
            if ($result['success']) {
                $stats['sent']++;
            } else {
                $stats['failed']++;
            }

            // Rate limiting: pause every 10 messages
            if (($stats['sent'] + $stats['failed']) % 10 === 0) {
                usleep(100000); // 100ms
            }
        }

        self::log('info', sprintf(
            'Broadcast SMS [%s]: %d sent, %d failed, %d skipped',
            $tag, $stats['sent'], $stats['failed'], $stats['skipped']
        ), $tag);

        return $stats;
    }

    /**
     * Send SMS to specific users by their BidSpirit email addresses.
     *
     * @param array  $userEmails       Array of BidSpirit user email addresses
     * @param string $notificationType The notification type for opt-in filtering
     * @param string $content          SMS message content
     * @param string $tag              Tag for analytics
     * @return array ['sent' => int, 'failed' => int, 'skipped' => int]
     */
    public static function sendToUsers($userEmails, $notificationType, $content, $tag = '')
    {
        $stats = ['sent' => 0, 'failed' => 0, 'skipped' => 0];

        if (!self::isNotificationEnabled($notificationType)) {
            return $stats;
        }

        if (empty($userEmails)) {
            return $stats;
        }

        $subscribers = SMSSubscriber::resolvePhonesByEmails($userEmails, $notificationType);
        if (empty($subscribers)) {
            return $stats;
        }

        foreach ($subscribers as $sub) {
            $phone = $sub->country_code . $sub->phone_number;
            $result = self::sendSMS($phone, $content, $tag);
            if ($result['success']) {
                $stats['sent']++;
            } else {
                $stats['failed']++;
            }
        }

        self::log('info', sprintf(
            'Targeted SMS [%s]: %d sent, %d failed (from %d users)',
            $tag, $stats['sent'], $stats['failed'], count($userEmails)
        ), $tag);

        return $stats;
    }

    /**
     * Send outbid SMS to a single user with cooldown protection.
     *
     * @param string $userEmail  BidSpirit email of the outbid user
     * @param string $itemId     BidSpirit item ID (for cooldown key)
     * @param string $content    SMS message content
     * @return array
     */
    public static function sendOutbidNotice($userEmail, $itemId, $content)
    {
        if (!self::isNotificationEnabled('outbid')) {
            return ['success' => false, 'error' => 'Outbid SMS disabled'];
        }

        // Cooldown check: prevent spam during active bidding
        $cooldownKey = 'af_sms_outbid_' . md5($userEmail . '_' . $itemId);
        $lastSent = get_transient($cooldownKey);
        if ($lastSent) {
            self::log('info', 'Outbid SMS cooldown active for ' . $userEmail . ' on item ' . $itemId);
            return ['success' => false, 'error' => 'Cooldown active'];
        }

        $subscribers = SMSSubscriber::resolvePhonesByEmails([$userEmail], 'outbid');
        if (empty($subscribers)) {
            return ['success' => false, 'error' => 'No SMS subscriber found for ' . $userEmail];
        }

        $sub = $subscribers[0];
        $phone = $sub->country_code . $sub->phone_number;
        $result = self::sendSMS($phone, $content, 'outbid');

        if ($result['success']) {
            // Set cooldown transient
            set_transient($cooldownKey, time(), self::OUTBID_COOLDOWN_SECONDS);
        }

        return $result;
    }

    // ────────────────────────────────────────────────────
    //  Message Builders
    // ────────────────────────────────────────────────────

    /**
     * Build SMS content for auction published event.
     *
     * @param string $auctionNumber
     * @param string $auctionName
     * @param string $url
     * @return string
     */
    public static function buildAuctionPublishedMessage($auctionNumber, $auctionName, $url)
    {
        $name = mb_substr($auctionName, 0, 60);
        return sprintf('New Auction #%s: %s. Browse the catalog now: %s', $auctionNumber, $name, $url);
    }

    /**
     * Build SMS content for auction starts soon event.
     *
     * @param string $auctionNumber
     * @param string $auctionName
     * @param string $url
     * @return string
     */
    public static function buildAuctionStartsSoonMessage($auctionNumber, $auctionName, $url)
    {
        $name = mb_substr($auctionName, 0, 60);
        return sprintf('Auction #%s starts soon! %s - Join live: %s', $auctionNumber, $name, $url);
    }

    /**
     * Build SMS content for item live soon event.
     *
     * @param string $itemIndex
     * @param string $itemName
     * @param string $auctionNumber
     * @param string $url
     * @return string
     */
    public static function buildItemLiveSoonMessage($itemIndex, $itemName, $auctionNumber, $url)
    {
        $name = mb_substr($itemName, 0, 50);
        return sprintf('Lot #%s (%s) is going live soon in Auction #%s! %s', $itemIndex, $name, $auctionNumber, $url);
    }

    /**
     * Build SMS content for outbid notice.
     *
     * @param string $itemIndex
     * @param string $itemName
     * @param string $url
     * @return string
     */
    public static function buildOutbidMessage($itemIndex, $itemName, $url)
    {
        $name = mb_substr($itemName, 0, 60);
        return sprintf("You've been outbid on Lot #%s (%s)! Bid again: %s", $itemIndex, $name, $url);
    }

    /**
     * Log SMS events using the plugin's debug system.
     *
     * @param string $level   'info', 'error', 'warning'
     * @param string $message
     * @param string $tag
     */
    private static function log($level, $message, $tag = '')
    {
        Debug::logData([
            'level'   => $level,
            'message' => $message,
            'tag'     => $tag,
            'time'    => date('Y-m-d H:i:s'),
        ], 'brevo_sms');
    }
}
