<?php

declare(strict_types=1);

namespace Instinctor\Services;

use Instinctor\Core\Config;
use Instinctor\Core\DB;


if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Gift Card Service
 * Manages digital gift cards with balance tracking
 */
class GiftCardService
{
    private static array $designs = [
        'default' => 'Classic',
        'birthday' => 'Birthday',
        'holiday' => 'Holiday',
        'receipt' => 'Thank You',
        'congrats' => 'Congratulations',
        'love' => 'With Love',
    ];

    /**
     * Generate unique gift card code
     */
    public static function generateCode(): string
    {
        $prefix = 'GC';
        $code = $prefix . strtoupper(wp_generate_password(12, false));
        
        $table = DB::prefix() . 'instinctor_gift_cards';
        
        // Ensure uniqueness
        while (DB::get_var(DB::prepare("SELECT id FROM $table WHERE code = %s", $code))) {
            $code = $prefix . strtoupper(wp_generate_password(12, false));
        }
        
        return $code;
    }

    /**
     * Create a new gift card
     */
    public static function create(array $data): ?object
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        $code = $data['code'] ?? self::generateCode();
        $amount = floatval($data['amount'] ?? 0);

        if ($amount <= 0) {
            return null;
        }

        $expiresAt = null;
        if (!empty($data['expires_days'])) {
            // phpcs:disable WordPress.DateTime.RestrictedFunctions -- date() used intentionally for local-timezone formatting; gmdate() would force UTC and change behavior.
            $expiresAt = date('Y-m-d H:i:s', strtotime('+' . intval($data['expires_days']) . ' days'));
            // phpcs:enable WordPress.DateTime.RestrictedFunctions
        } elseif (!empty($data['expires_at'])) {
            $expiresAt = $data['expires_at'];
        }

        $deliverAt = null;
        if (!empty($data['deliver_at'])) {
            $deliverAt = $data['deliver_at'];
        }

        DB::insert($table, [
            'code' => $code,
            'initial_balance' => $amount,
            'current_balance' => $amount,
            'currency' => sanitize_text_field($data['currency'] ?? 'USD'),
            'sender_name' => sanitize_text_field($data['sender_name'] ?? ''),
            'sender_email' => sanitize_email($data['sender_email'] ?? ''),
            'recipient_name' => sanitize_text_field($data['recipient_name'] ?? ''),
            'recipient_email' => sanitize_email($data['recipient_email'] ?? ''),
            'message' => sanitize_textarea_field($data['message'] ?? ''),
            'design' => sanitize_text_field($data['design'] ?? 'default'),
            'order_id' => !empty($data['order_id']) ? intval($data['order_id']) : null,
            'deliver_at' => $deliverAt,
            'expires_at' => $expiresAt,
            'is_delivered' => empty($data['recipient_email']) ? 1 : 0,
        ]);

        $id = DB::insert_id();
        
        // Log initial credit
        self::logTransaction($id, $amount, 'credit', null, 'Gift card created');

        return self::getById($id);
    }

    /**
     * Get gift card by ID
     */
    public static function getById(int $id): ?object
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        return DB::get_row(DB::prepare("SELECT * FROM $table WHERE id = %d", $id));
    }

    /**
     * Get gift card by code
     */
    public static function getByCode(string $code): ?object
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        return DB::get_row(DB::prepare("SELECT * FROM $table WHERE code = %s", strtoupper($code)));
    }

    /**
     * Check if gift card is valid for use
     */
    public static function isValid(string $code): array
    {
        $card = self::getByCode($code);

        if (!$card) {
            return ['valid' => false, 'error' => 'Gift card not found'];
        }

        if ($card->status !== 'active') {
            return ['valid' => false, 'error' => 'Gift card is ' . $card->status];
        }

        if ($card->current_balance <= 0) {
            return ['valid' => false, 'error' => 'Gift card has no remaining balance'];
        }

        if ($card->expires_at && strtotime($card->expires_at) < time()) {
            self::updateStatus($card->id, 'expired');
            return ['valid' => false, 'error' => 'Gift card has expired'];
        }

        return ['valid' => true, 'card' => $card];
    }

    /**
     * Apply gift card to order
     */
    public static function apply(string $code, float $orderTotal, int $orderId): array
    {
        $validation = self::isValid($code);
        
        if (!$validation['valid']) {
            return $validation;
        }

        $card = $validation['card'];
        $amountToUse = min($card->current_balance, $orderTotal);
        $newBalance = $card->current_balance - $amountToUse;

        $table = DB::prefix() . 'instinctor_gift_cards';

        DB::update($table, [
            'current_balance' => $newBalance,
            'status' => $newBalance <= 0 ? 'used' : 'active',
        ], ['id' => $card->id]);

        self::logTransaction($card->id, -$amountToUse, 'debit', $orderId, 'Applied to order #' . $orderId);

        return [
            'valid' => true,
            'amount_applied' => $amountToUse,
            'remaining_balance' => $newBalance,
            'card' => self::getById($card->id),
        ];
    }

    /**
     * Refund amount back to gift card
     */
    public static function refund(int $cardId, float $amount, int $orderId): bool
    {
        $card = self::getById($cardId);
        
        if (!$card) {
            return false;
        }

        $table = DB::prefix() . 'instinctor_gift_cards';

        $newBalance = $card->current_balance + $amount;

        DB::update($table, [
            'current_balance' => $newBalance,
            'status' => 'active',
        ], ['id' => $cardId]);

        self::logTransaction($cardId, $amount, 'refund', $orderId, 'Refund from order #' . $orderId);

        return true;
    }

    /**
     * Use gift card balance (wrapper for apply)
     */
    public static function useBalance(int $cardId, float $amount, int $orderId): bool
    {
        $card = self::getById($cardId);
        
        if (!$card || $card->current_balance <= 0) {
            return false;
        }

        $amountToUse = min($card->current_balance, $amount);
        $newBalance = $card->current_balance - $amountToUse;

        $table = DB::prefix() . 'instinctor_gift_cards';

        DB::update($table, [
            'current_balance' => $newBalance,
            'status' => $newBalance <= 0 ? 'used' : 'active',
        ], ['id' => $cardId]);

        self::logTransaction($cardId, -$amountToUse, 'debit', $orderId, 'Applied to order');

        return true;
    }

    /**
     * Check balance
     */
    public static function checkBalance(string $code): array
    {
        $card = self::getByCode($code);

        if (!$card) {
            return ['found' => false];
        }

        return [
            'found' => true,
            'code' => $card->code,
            'balance' => $card->current_balance,
            'currency' => $card->currency,
            'status' => $card->status,
            'expires_at' => $card->expires_at,
        ];
    }

    /**
     * Log transaction
     */
    private static function logTransaction(int $cardId, float $amount, string $type, ?int $orderId = null, ?string $note = null): void
    {
        $table = DB::prefix() . 'instinctor_gift_card_transactions';

        $card = self::getById($cardId);

        DB::insert($table, [
            'gift_card_id' => $cardId,
            'order_id' => $orderId,
            'amount' => $amount,
            'balance_after' => $card->current_balance,
            'type' => $type,
            'note' => $note,
        ]);
    }

    /**
     * Get transaction history
     */
    public static function getTransactions(int $cardId): array
    {
        $table = DB::prefix() . 'instinctor_gift_card_transactions';

        return DB::get_results(DB::prepare(
            "SELECT * FROM $table WHERE gift_card_id = %d ORDER BY created_at DESC",
            $cardId
        ));
    }

    /**
     * Update gift card status
     */
    public static function updateStatus(int $id, string $status): bool
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        return DB::update($table, ['status' => $status], ['id' => $id]) !== false;
    }

    /**
     * Get all gift cards
     */
    public static function getAll(array $args = []): array
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        $where = '1=1';
        $params = [];

        if (!empty($args['status'])) {
            $where .= ' AND status = %s';
            $params[] = $args['status'];
        }

        if (!empty($args['search'])) {
            $where .= ' AND (code LIKE %s OR recipient_email LIKE %s)';
            $search = '%' . DB::esc_like($args['search']) . '%';
            $params[] = $search;
            $params[] = $search;
        }

        $limit = intval($args['limit'] ?? 50);
        $offset = intval($args['offset'] ?? 0);

        $sql = "SELECT * FROM $table WHERE $where ORDER BY created_at DESC LIMIT %d OFFSET %d";
        $params[] = $limit;
        $params[] = $offset;

        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Query uses $wpdb->prepare with safe params
        return DB::get_results(DB::prepare($sql, ...$params));
    }

    /**
     * Get cards pending delivery
     */
    public static function getPendingDelivery(): array
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        return DB::get_results(
            "SELECT * FROM $table 
             WHERE is_delivered = 0 
             AND recipient_email != '' 
             AND (deliver_at IS NULL OR deliver_at <= NOW())
             AND status = 'active'"
        );
    }

    /**
     * Mark as delivered
     */
    public static function markDelivered(int $id): bool
    {
        $table = DB::prefix() . 'instinctor_gift_cards';

        return DB::update($table, ['is_delivered' => 1], ['id' => $id]) !== false;
    }

    /**
     * Send gift card email
     */
    public static function sendEmail(int $cardId): bool
    {
        $card = self::getById($cardId);
        
        if (!$card || !$card->recipient_email) {
            return false;
        }

        // Use EmailService template
        $sent = EmailService::send('gift_card_received', $card->recipient_email, [
            'sender_name' => $card->sender_name ?: get_bloginfo('name'),
            'recipient_name' => $card->recipient_name ?: '',
            'amount' => CurrencyService::formatPrice($card->initial_balance),
            'personal_message' => $card->message ?: '',
            'gift_card_code' => $card->code,
            'expires_at' => $card->expires_at ? wp_date('F j, Y', strtotime($card->expires_at)) : __('Never', 'instinctor'),
        ]);

        if ($sent) {
            self::markDelivered($cardId);
        }

        return $sent;
    }

    /**
     * Get available designs
     */
    public static function getDesigns(): array
    {
        return self::$designs;
    }

    /**
     * Process scheduled deliveries (cron job)
     */
    public static function processScheduledDeliveries(): int
    {
        $pending = self::getPendingDelivery();
        $sent = 0;

        foreach ($pending as $card) {
            if (self::sendEmail($card->id)) {
                $sent++;
            }
        }

        return $sent;
    }
}
