<?php

/**
 * Subscription Manager - Handles plans, billing, and subscription lifecycle
 */
class SubscriptionManager {
    private static $billingConfig = null;

    private static function getBillingConfig(): array
    {
        if (self::$billingConfig !== null) {
            return self::$billingConfig;
        }

        $configPath = __DIR__ . '/../config/billing_plans.php';
        if (file_exists($configPath)) {
            $config = require $configPath;
            self::$billingConfig = is_array($config) ? $config : [];
        } else {
            self::$billingConfig = [];
        }

        return self::$billingConfig;
    }

    private static function loadPlanConfig(): array
    {
        $config = self::getBillingConfig();
        return $config['plans'] ?? [];
    }

    private static function normalizeConfigPlan(array $plan): array
    {
        $plan['features'] = array_values($plan['features'] ?? []);
        $plan['limits'] = $plan['limits'] ?? [];
        return $plan;
    }

    private static function getConfigPlanById(int $planId): ?array
    {
        foreach (self::loadPlanConfig() as $plan) {
            if ((int) ($plan['id'] ?? 0) === $planId) {
                return self::normalizeConfigPlan($plan);
            }
        }

        return null;
    }

    public static function getCurrencyCode(): string
    {
        $config = self::getBillingConfig();
        return strtoupper($config['currency'] ?? 'USD');
    }

    public static function getCurrencySymbol(): string
    {
        $code = self::getCurrencyCode();
        switch ($code) {
            case 'EUR':
                return '€';
            case 'GBP':
                return '£';
            case 'INR':
                return '₹';
            case 'AUD':
            case 'CAD':
            case 'USD':
                return '$';
            default:
                return '$';
        }
    }

    public static function getPlanName(int $planId): string
    {
        $plan = self::getPlan($planId);
        return $plan['name'] ?? 'Plan';
    }

    public static function getPlanPrice(int $planId, string $billingCycle = 'monthly'): float
    {
        $plan = self::getPlan($planId);
        if (!$plan) {
            return 0.0;
        }

        $cycle = $billingCycle === 'yearly' ? 'yearly' : 'monthly';
        return $cycle === 'yearly'
            ? (float) ($plan['price_yearly'] ?? 0)
            : (float) ($plan['price_monthly'] ?? 0);
    }

    public static function getPlanFeatures(int $planId): array
    {
        $plan = self::getPlan($planId);
        return $plan['features'] ?? [];
    }

    /**
     * Get all available subscription plans from database
     * Database takes priority over config file for dynamic admin management
     */
    public static function getPlans(): array {
        $pdo = getPDO();

        $stmt = $pdo->prepare("
            SELECT * FROM subscription_plans 
            WHERE is_active = 1 
            ORDER BY sort_order ASC, price_monthly ASC
        ");

        $stmt->execute();
        $plans = $stmt->fetchAll();

        // If database has plans, use them
        if (!empty($plans)) {
            foreach ($plans as &$plan) {
                $plan['features'] = json_decode($plan['features'] ?? '[]', true);
                $plan['limits'] = json_decode($plan['limits'] ?? '{}', true);
            }
            return $plans;
        }

        // Fallback to config if no database plans
        $configPlans = self::loadPlanConfig();
        if (!empty($configPlans)) {
            return array_map(function ($plan) {
                return self::normalizeConfigPlan($plan);
            }, $configPlans);
        }

        return [];
    }
    
    /**
     * Get plan by ID - Database takes priority over config
     */
    public static function getPlan(int $planId): ?array {
        $pdo = getPDO();

        // Try database first
        $stmt = $pdo->prepare("SELECT * FROM subscription_plans WHERE id = ?");
        $stmt->execute([$planId]);
        $plan = $stmt->fetch();

        if ($plan) {
            $plan['features'] = json_decode($plan['features'] ?? '[]', true);
            $plan['limits'] = json_decode($plan['limits'] ?? '{}', true);
            return $plan;
        }

        // Fallback to config
        $configPlan = self::getConfigPlanById($planId);
        if ($configPlan) {
            return $configPlan;
        }

        return null;
    }
    
    /**
     * Get company's current subscription
     */
    public static function getCompanySubscription(int $companyId): ?array {
        $pdo = getPDO();
        
        $stmt = $pdo->prepare("
            SELECT c.*, sp.name as plan_name, sp.price_monthly, sp.price_yearly, 
                   sp.features, sp.limits, sp.slug as plan_slug
            FROM companies c
            LEFT JOIN subscription_plans sp ON c.plan_id = sp.id
            WHERE c.id = ?
        ");
        
        $stmt->execute([$companyId]);
        $subscription = $stmt->fetch();
        
        if ($subscription) {
            $subscription['features'] = json_decode($subscription['features'] ?? '[]', true);
            $subscription['limits'] = json_decode($subscription['limits'] ?? '{}', true);

            if (!empty($subscription['plan_id'])) {
                $configPlan = self::getPlan((int) $subscription['plan_id']);
                if ($configPlan) {
                    $subscription['plan_name'] = $configPlan['name'] ?? $subscription['plan_name'];
                    $subscription['price_monthly'] = $configPlan['price_monthly'] ?? $subscription['price_monthly'];
                    $subscription['price_yearly'] = $configPlan['price_yearly'] ?? $subscription['price_yearly'];
                    $subscription['features'] = $configPlan['features'] ?? $subscription['features'];
                    $subscription['limits'] = $configPlan['limits'] ?? $subscription['limits'];
                }
            }
        }
        
        return $subscription;
    }
    
    /**
     * Create or update subscription
     */
    public static function createSubscription(int $companyId, int $planId, string $billingCycle, array $paymentData): array {
        $pdo = getPDO();
        $pdo->beginTransaction();
        
        try {
            $plan = self::getPlan($planId);
            if (!$plan) {
                throw new Exception('Invalid plan selected');
            }
            
            $price = $billingCycle === 'yearly' ? $plan['price_yearly'] : $plan['price_monthly'];
            $expiresAt = date('Y-m-d H:i:s', strtotime('+1 ' . ($billingCycle === 'yearly' ? 'year' : 'month')));
            
            // Update company subscription
            $stmt = $pdo->prepare("
                UPDATE companies 
                SET plan_id = ?, subscription_status = 'active', subscription_expires_at = ?, 
                    billing_cycle = ?, stripe_customer_id = ?, razorpay_customer_id = ?
                WHERE id = ?
            ");
            
            $stmt->execute([
                $planId,
                $expiresAt,
                $billingCycle,
                $paymentData['stripe_customer_id'] ?? null,
                $paymentData['razorpay_customer_id'] ?? null,
                $companyId
            ]);
            
            // Create billing invoice
            $invoiceNumber = self::generateInvoiceNumber();
            
            $invoiceStmt = $pdo->prepare("
                INSERT INTO billing_invoices (
                    company_id, invoice_number, amount, currency, status, due_date, 
                    billing_period_start, billing_period_end, line_items
                ) VALUES (?, ?, ?, ?, 'sent', ?, ?, ?, ?)
            ");
            
            $lineItems = json_encode([
                [
                    'description' => "{$plan['name']} Plan - " . ucfirst($billingCycle),
                    'quantity' => 1,
                    'unit_price' => $price,
                    'total' => $price
                ]
            ]);
            
            $invoiceStmt->execute([
                $companyId,
                $invoiceNumber,
                $price,
                'USD',
                date('Y-m-d', strtotime('+7 days')), // Due in 7 days
                date('Y-m-d'),
                date('Y-m-d', strtotime('+1 ' . ($billingCycle === 'yearly' ? 'year' : 'month'))),
                $lineItems
            ]);
            
            $pdo->commit();
            
            return [
                'success' => true,
                'subscription_expires_at' => $expiresAt,
                'invoice_number' => $invoiceNumber,
                'amount' => $price
            ];
            
        } catch (Exception $e) {
            $pdo->rollback();
            throw $e;
        }
    }
    
    /**
     * Cancel subscription
     */
    public static function cancelSubscription(int $companyId, bool $immediate = true): bool {
        $pdo = getPDO();
        $current = self::getCompanySubscription($companyId);

        $expiresAt = $immediate
            ? date('Y-m-d H:i:s')
            : ($current['subscription_expires_at'] ?? date('Y-m-d H:i:s'));

        $stmt = $pdo->prepare("
            UPDATE companies 
            SET subscription_status = 'canceled', subscription_expires_at = ?
            WHERE id = ?
        ");

        return $stmt->execute([$expiresAt, $companyId]);
    }
    
    /**
     * Update subscription plan
     */
    public static function updateSubscriptionPlan(int $companyId, int $newPlanId, string $billingCycle): array {
        $pdo = getPDO();
        
        $currentSubscription = self::getCompanySubscription($companyId);
        $newPlan = self::getPlan($newPlanId);
        
        if (!$currentSubscription || !$newPlan) {
            throw new Exception('Invalid subscription or plan');
        }
        
        // Calculate proration if upgrading
        $price = $billingCycle === 'yearly' ? $newPlan['price_yearly'] : $newPlan['price_monthly'];
        
        $stmt = $pdo->prepare("
            UPDATE companies 
            SET plan_id = ?, billing_cycle = ?, subscription_status = 'active'
            WHERE id = ?
        ");
        
        $stmt->execute([$newPlanId, $billingCycle, $companyId]);
        
        return [
            'success' => true,
            'new_plan' => $newPlan,
            'price' => $price
        ];
    }

    /**
     * Renew subscription for next billing cycle
     */
    public static function renewSubscription(int $companyId, ?string $billingCycle = null): array
    {
        $pdo = getPDO();
        $currentSubscription = self::getCompanySubscription($companyId);

        if (!$currentSubscription || empty($currentSubscription['plan_id'])) {
            throw new Exception('No active plan to renew.');
        }

        $planId = (int) $currentSubscription['plan_id'];
        $plan = self::getPlan($planId);

        if (!$plan) {
            throw new Exception('Unable to load plan configuration.');
        }

        $cycle = $billingCycle === 'yearly' ? 'yearly' : ($billingCycle === 'monthly' ? 'monthly' : ($currentSubscription['billing_cycle'] ?? 'monthly'));
        $interval = $cycle === 'yearly' ? '+1 year' : '+1 month';
        $expiresAt = date('Y-m-d H:i:s', strtotime($interval));
        $price = $cycle === 'yearly' ? ($plan['price_yearly'] ?? 0) : ($plan['price_monthly'] ?? 0);

        $stmt = $pdo->prepare("
            UPDATE companies 
            SET plan_id = ?, billing_cycle = ?, subscription_status = 'active', subscription_expires_at = ?
            WHERE id = ?
        ");

        $stmt->execute([$planId, $cycle, $expiresAt, $companyId]);

        return [
            'success' => true,
            'plan' => $plan,
            'billing_cycle' => $cycle,
            'subscription_expires_at' => $expiresAt,
            'amount' => $price,
        ];
    }
    
    /**
     * Process webhook from payment provider
     */
    public static function processWebhook(string $provider, array $payload): bool {
        try {
            switch ($provider) {
                case 'stripe':
                    return self::processStripeWebhook($payload);
                case 'razorpay':
                    return self::processRazorpayWebhook($payload);
                default:
                    return false;
            }
        } catch (Exception $e) {
            error_log("Webhook processing error: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Process Stripe webhook
     */
    private static function processStripeWebhook(array $payload): bool {
        $eventType = $payload['type'] ?? '';
        
        switch ($eventType) {
            case 'invoice.payment_succeeded':
                return self::handlePaymentSucceeded($payload['data']['object']);
                
            case 'invoice.payment_failed':
                return self::handlePaymentFailed($payload['data']['object']);
                
            case 'customer.subscription.deleted':
                return self::handleSubscriptionDeleted($payload['data']['object']);
                
            default:
                return true; // Acknowledge other events
        }
    }
    
    /**
     * Process Razorpay webhook
     */
    private static function processRazorpayWebhook(array $payload): bool {
        $eventType = $payload['event'] ?? '';
        
        switch ($eventType) {
            case 'invoice.paid':
                return self::handlePaymentSucceeded($payload['payload']['invoice']['entity']);
                
            case 'payment.failed':
                return self::handlePaymentFailed($payload['payload']['payment']['entity']);
                
            default:
                return true;
        }
    }
    
    /**
     * Handle successful payment
     */
    private static function handlePaymentSucceeded(array $invoiceData): bool {
        $pdo = getPDO();
        
        // Find company by customer ID
        $customerId = $invoiceData['customer'] ?? '';
        
        $stmt = $pdo->prepare("SELECT id FROM companies WHERE stripe_customer_id = ? OR razorpay_customer_id = ?");
        $stmt->execute([$customerId, $customerId]);
        $company = $stmt->fetch();
        
        if ($company) {
            // Update subscription status
            $newExpiresAt = date('Y-m-d H:i:s', strtotime('+1 month'));
            
            $updateStmt = $pdo->prepare("
                UPDATE companies 
                SET subscription_status = 'active', subscription_expires_at = ?
                WHERE id = ?
            ");
            
            $updateStmt->execute([$newExpiresAt, $company['id']]);
            
            // Mark invoice as paid
            if (isset($invoiceData['id'])) {
                $invoiceStmt = $pdo->prepare("
                    UPDATE billing_invoices 
                    SET status = 'paid', paid_at = NOW(), stripe_invoice_id = ?
                    WHERE company_id = ? AND status = 'sent'
                ");
                
                $invoiceStmt->execute([$invoiceData['id'], $company['id']]);
            }
            
            return true;
        }
        
        return false;
    }
    
    /**
     * Handle failed payment
     */
    private static function handlePaymentFailed(array $paymentData): bool {
        $pdo = getPDO();
        
        // Find company by customer ID
        $customerId = $paymentData['customer'] ?? '';
        
        $stmt = $pdo->prepare("SELECT id FROM companies WHERE stripe_customer_id = ? OR razorpay_customer_id = ?");
        $stmt->execute([$customerId, $customerId]);
        $company = $stmt->fetch();
        
        if ($company) {
            // Update subscription status
            $updateStmt = $pdo->prepare("
                UPDATE companies 
                SET subscription_status = 'past_due'
                WHERE id = ?
            ");
            
            $updateStmt->execute([$company['id']]);
            
            return true;
        }
        
        return false;
    }
    
    /**
     * Handle subscription deletion
     */
    private static function handleSubscriptionDeleted(array $subscriptionData): bool {
        $pdo = getPDO();
        
        $customerId = $subscriptionData['customer'] ?? '';
        
        $stmt = $pdo->prepare("SELECT id FROM companies WHERE stripe_customer_id = ? OR razorpay_customer_id = ?");
        $stmt->execute([$customerId, $customerId]);
        $company = $stmt->fetch();
        
        if ($company) {
            // Cancel subscription
            $updateStmt = $pdo->prepare("
                UPDATE companies 
                SET subscription_status = 'canceled'
                WHERE id = ?
            ");
            
            $updateStmt->execute([$company['id']]);
            
            return true;
        }
        
        return false;
    }
    
    /**
     * Generate unique invoice number
     */
    private static function generateInvoiceNumber(): string {
        $pdo = getPDO();
        
        $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM billing_invoices WHERE MONTH(created_at) = MONTH(CURRENT_DATE)");
        $stmt->execute();
        $result = $stmt->fetch();
        
        $sequence = ($result['count'] ?? 0) + 1;
        
        return 'INV-' . date('Y-m') . '-' . str_pad($sequence, 4, '0', STR_PAD_LEFT);
    }
    
    /**
     * Get billing history for company
     */
    public static function getBillingHistory(int $companyId): array {
        $pdo = getPDO();
        
        $stmt = $pdo->prepare("
            SELECT * FROM billing_invoices 
            WHERE company_id = ? 
            ORDER BY created_at DESC
        ");
        
        $stmt->execute([$companyId]);
        $invoices = $stmt->fetchAll();
        
        foreach ($invoices as &$invoice) {
            $invoice['line_items'] = json_decode($invoice['line_items'] ?? '[]', true);
        }
        
        return $invoices;
    }
    
    /**
     * Check if company can add more users based on plan limits
     */
    public static function canAddUser(int $companyId): bool {
        $subscription = self::getCompanySubscription($companyId);
        
        if (!$subscription) {
            return false;
        }
        
        $userLimit = $subscription['limits']['users'] ?? 0;
        
        if ($userLimit === -1) {
            return true; // Unlimited
        }
        
        $pdo = getPDO();
        $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM users WHERE company_id = ? AND is_active = 1");
        $stmt->execute([$companyId]);
        $currentUsers = $stmt->fetch()['count'];
        
        return $currentUsers < $userLimit;
    }
}
