<?php

declare(strict_types=1);

namespace Instinctor\Services;

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


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

/**
 * Coupon Service
 * Handles coupon validation, application, and management
 */
class CouponService
{
    public function getCoupon(string $code): ?object
    {
        $table = DB::prefix() . 'instinctor_coupons';

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

    public function getCouponById(int $id): ?object
    {
        $table = DB::prefix() . 'instinctor_coupons';

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

    public function validateCoupon(string $code, array $cart, ?string $email = null): array
    {
        $coupon = $this->getCoupon($code);

        if (!$coupon) {
            return ['valid' => false, 'error' => __('Invalid coupon code.', 'instinctor')];
        }

        // Check status
        if ($coupon->status !== 'active') {
            return ['valid' => false, 'error' => __('This coupon is no longer active.', 'instinctor')];
        }

        // Check dates
        $now = current_time('mysql');
        if ($coupon->start_date && $now < $coupon->start_date) {
            return ['valid' => false, 'error' => __('This coupon is not yet valid.', 'instinctor')];
        }
        if ($coupon->end_date && $now > $coupon->end_date) {
            return ['valid' => false, 'error' => __('This coupon has expired.', 'instinctor')];
        }

        // Check usage limit
        if ($coupon->usage_limit && $coupon->usage_count >= $coupon->usage_limit) {
            return ['valid' => false, 'error' => __('This coupon has reached its usage limit.', 'instinctor')];
        }

        // Check per-user limit
        if ($coupon->usage_limit_per_user && $email) {
            $userUsage = $this->getUserUsageCount($coupon->id, $email);
            if ($userUsage >= $coupon->usage_limit_per_user) {
                return ['valid' => false, 'error' => __('You have already used this coupon.', 'instinctor')];
            }
        }

        // Check minimum amount
        $cartTotal = $cart['totals']['subtotal'] ?? 0;
        if ($coupon->minimum_amount && $cartTotal < $coupon->minimum_amount) {
            return [
                'valid' => false, 
                /* translators: placeholder values */
                'error' => sprintf(__('Minimum spend of $%.2f required.', 'instinctor'), $coupon->minimum_amount)
            ];
        }

        // Check maximum amount
        if ($coupon->maximum_amount && $cartTotal > $coupon->maximum_amount) {
            return [
                /* translators: placeholder values */
                'valid' => false,
                /* translators: placeholder values */
                'error' => sprintf(__('Maximum cart total of $%.2f for this coupon.', 'instinctor'), $coupon->maximum_amount)
            ];
        }

        // Check product restrictions
        if ($coupon->product_ids) {
            $allowedProducts = array_map('intval', explode(',', $coupon->product_ids));
            $cartProductIds = array_column($cart['items'] ?? [], 'product_id');
            if (!array_intersect($cartProductIds, $allowedProducts)) {
                return ['valid' => false, 'error' => __('This coupon is not valid for items in your cart.', 'instinctor')];
            }
        }

        // Check excluded products
        if ($coupon->excluded_product_ids) {
            $excludedProducts = array_map('intval', explode(',', $coupon->excluded_product_ids));
            $cartProductIds = array_column($cart['items'] ?? [], 'product_id');
            if (count(array_intersect($cartProductIds, $excludedProducts)) === count($cartProductIds)) {
                return ['valid' => false, 'error' => __('This coupon cannot be applied to items in your cart.', 'instinctor')];
            }
        }

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

    public function calculateDiscount(object $coupon, array $cart): float
    {
        $subtotal = $cart['totals']['subtotal'] ?? 0;
        $discount = 0;

        switch ($coupon->discount_type) {
            case 'percent':
                $discount = $subtotal * ($coupon->amount / 100);
                break;
                
            case 'fixed_cart':
                $discount = min($coupon->amount, $subtotal);
                break;
                
            case 'fixed_product':
                // Apply to each eligible item
                foreach ($cart['items'] ?? [] as $item) {
                    if ($this->isProductEligible($coupon, $item['product_id'])) {
                        $discount += $coupon->amount * $item['quantity'];
                    }
                }
                $discount = min($discount, $subtotal);
                break;
        }

        return round($discount, 2);
    }

    private function isProductEligible(object $coupon, int $productId): bool
    {
        // Check excluded products
        if ($coupon->excluded_product_ids) {
            $excluded = array_map('intval', explode(',', $coupon->excluded_product_ids));
            if (in_array($productId, $excluded)) {
                return false;
            }
        }

        // Check allowed products (if specified)
        if ($coupon->product_ids) {
            $allowed = array_map('intval', explode(',', $coupon->product_ids));
            return in_array($productId, $allowed);
        }

        return true;
    }

    public function applyCoupon(string $code, array $cart, ?string $email = null): array
    {
        $validation = $this->validateCoupon($code, $cart, $email);
        
        if (!$validation['valid']) {
            return $validation;
        }

        $coupon = $validation['coupon'];
        $discount = $this->calculateDiscount($coupon, $cart);

        return [
            'valid' => true,
            'coupon' => [
                'id' => $coupon->id,
                'code' => $coupon->code,
                'discount_type' => $coupon->discount_type,
                'amount' => $coupon->amount,
                'shipping' => (bool) $coupon->shipping,
            ],
            'discount' => $discount,
        ];
    }

    public function recordUsage(int $couponId, int $orderId, ?int $customerId, ?string $email, float $discountAmount): void
    {
        $p = DB::prefix();

        // Update usage count
        DB::query(DB::prepare(
            "UPDATE {$p}instinctor_coupons SET usage_count = usage_count + 1 WHERE id = %d",
            $couponId
        ));
    }

    private function getUserUsageCount(int $couponId, string $email): int
    {
        $p = DB::prefix();

        // Get coupon code
        $couponCode = DB::get_var(DB::prepare(
            "SELECT code FROM {$p}instinctor_coupons WHERE id = %d",
            $couponId
        ));

        if (!$couponCode) {
            return 0;
        }

        // Count orders by this customer that used this coupon (stored in order meta)
        return (int) DB::get_var(DB::prepare(
            "SELECT COUNT(*) FROM {$p}instinctor_orders o
             JOIN {$p}instinctor_customers c ON o.customer_id = c.id
             WHERE c.email = %s
             AND o.meta LIKE %s
             AND o.status NOT IN ('cancelled', 'failed')",
            $email,
            '%' . DB::esc_like('"coupon_code":"' . $couponCode . '"') . '%'
        ));
    }

    // Admin methods
    public function getAllCoupons(array $args = []): array
    {
        $table = DB::prefix() . 'instinctor_coupons';

        $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 description LIKE %s)';
            $params[] = '%' . DB::esc_like($args['search']) . '%';
            $params[] = '%' . DB::esc_like($args['search']) . '%';
        }

        $orderby = $args['orderby'] ?? 'created_at';
        $order = $args['order'] ?? 'DESC';
        $limit = $args['limit'] ?? 50;
        $offset = $args['offset'] ?? 0;

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

        if (!empty($params)) {
            $sql = DB::prepare($sql, ...$params);
        }

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

    public function createCoupon(array $data): int
    {
        $table = DB::prefix() . 'instinctor_coupons';

        DB::insert($table, [
            'code' => strtoupper(sanitize_text_field($data['code'])),
            'description' => sanitize_textarea_field($data['description'] ?? ''),
            'discount_type' => $data['discount_type'] ?? 'percent',
            'amount' => floatval($data['amount'] ?? 0),
            'shipping' => !empty($data['shipping']) ? 1 : 0,
            'minimum_amount' => !empty($data['minimum_amount']) ? floatval($data['minimum_amount']) : null,
            'maximum_amount' => !empty($data['maximum_amount']) ? floatval($data['maximum_amount']) : null,
            'usage_limit' => !empty($data['usage_limit']) ? intval($data['usage_limit']) : null,
            'usage_limit_per_user' => !empty($data['usage_limit_per_user']) ? intval($data['usage_limit_per_user']) : null,
            'product_ids' => $data['product_ids'] ?? null,
            'excluded_product_ids' => $data['excluded_product_ids'] ?? null,
            'start_date' => !empty($data['start_date']) ? $data['start_date'] : null,
            'end_date' => !empty($data['end_date']) ? $data['end_date'] : null,
            'status' => $data['status'] ?? 'active',
        ]);

        return (int) DB::insert_id();
    }

    public function updateCoupon(int $id, array $data): bool
    {
        $table = DB::prefix() . 'instinctor_coupons';

        $update = [];

        if (isset($data['code'])) {
            $update['code'] = strtoupper(sanitize_text_field($data['code']));
        }
        if (isset($data['description'])) {
            $update['description'] = sanitize_textarea_field($data['description']);
        }
        if (isset($data['discount_type'])) {
            $update['discount_type'] = $data['discount_type'];
        }
        if (isset($data['amount'])) {
            $update['amount'] = floatval($data['amount']);
        }
        if (isset($data['shipping'])) {
            $update['shipping'] = !empty($data['shipping']) ? 1 : 0;
        }
        if (array_key_exists('minimum_amount', $data)) {
            $update['minimum_amount'] = !empty($data['minimum_amount']) ? floatval($data['minimum_amount']) : null;
        }
        if (array_key_exists('maximum_amount', $data)) {
            $update['maximum_amount'] = !empty($data['maximum_amount']) ? floatval($data['maximum_amount']) : null;
        }
        if (array_key_exists('usage_limit', $data)) {
            $update['usage_limit'] = !empty($data['usage_limit']) ? intval($data['usage_limit']) : null;
        }
        if (array_key_exists('usage_limit_per_user', $data)) {
            $update['usage_limit_per_user'] = !empty($data['usage_limit_per_user']) ? intval($data['usage_limit_per_user']) : null;
        }
        if (array_key_exists('start_date', $data)) {
            $update['start_date'] = !empty($data['start_date']) ? $data['start_date'] : null;
        }
        if (array_key_exists('end_date', $data)) {
            $update['end_date'] = !empty($data['end_date']) ? $data['end_date'] : null;
        }
        if (isset($data['status'])) {
            $update['status'] = $data['status'];
        }

        if (empty($update)) {
            return false;
        }

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

    public function deleteCoupon(int $id): bool
    {
        return DB::delete(DB::prefix() . 'instinctor_coupons', ['id' => $id]) !== false;
    }

    public function generateCode(int $length = 8): string
    {
        $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
        $code = '';
        for ($i = 0; $i < $length; $i++) {
            $code .= $chars[random_int(0, strlen($chars) - 1)];
        }
        return $code;
    }
}
