<?php

/**
 * Dashboard Controller V1
 *
 * Dense business intelligence dashboard for subscription/licensing merchants.
 * Revenue-first, licensing-aware, Stripe-inspired density.
 *
 * @package Licenzo\Http\Controller\Api\V1
 */

namespace Licenzo\Http\Controller\Api\V1;

use Licenzo\Framework\Http\Request;
use Licenzo\Model\License;
use Licenzo\Model\LicenseActivation;
use Licenzo\Model\LicenseEvent;
use Licenzo\Model\Product;
use Licenzo\Model\Subscription;
use Licenzo\Model\SubscriptionEvent;
use Licenzo\Model\SubscriptionTransaction;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Dashboard API controller — all statistics and overview data
 */
class DashboardController
{
    /**
     * Allowed periods for dashboard queries
     */
    private const ALLOWED_PERIODS = [7, 14, 30];

    /**
     * Charge transaction types (exclude refunds from revenue)
     */
    private const CHARGE_TYPES = [
        SubscriptionTransaction::TYPE_PURCHASE,
        SubscriptionTransaction::TYPE_RENEWAL,
        SubscriptionTransaction::TYPE_UPGRADE,
        SubscriptionTransaction::TYPE_DOWNGRADE,
        SubscriptionTransaction::TYPE_CHARGE,
    ];

    /**
     * Get dashboard KPI statistics
     *
     * Returns: revenue, MRR, active subscriptions, active licenses, active sites.
     *
     * Revenue sourced from transaction ledger (canonical).
     * MRR normalized from active subscription recurring_amount by billing_cycle.
     *
     * @return array
     */
    public function stats()
    {
        // Revenue: sum of successful charge transactions (returns raw cents, bypasses MoneyCast)
        $totalRevenueCents = (int) SubscriptionTransaction::where('status', SubscriptionTransaction::STATUS_SUCCESS)
            ->whereIn('transaction_type', self::CHARGE_TYPES)
            ->sum('total');

        // MRR: active subscription recurring_amount normalized to monthly (cents)
        $mrrCents = $this->calculateMrr();

        // Active subscriptions
        $activeSubscriptions = Subscription::where('status', Subscription::STATUS_ACTIVE)->count();

        // Active licenses
        $activeLicenses = License::where('status', License::STATUS_ACTIVE)->count();

        // Active sites (license activations)
        $activeSites = LicenseActivation::where('status', LicenseActivation::STATUS_ACTIVE)->count();

        return [
            'success' => true,
            'data' => [
                'revenue' => $totalRevenueCents,
                'mrr' => $mrrCents,
                'active_subscriptions' => $activeSubscriptions,
                'active_licenses' => $activeLicenses,
                'active_sites' => $activeSites,
            ]
        ];
    }

    /**
     * Get revenue chart data — daily revenue over configurable period
     *
     * Each data point is the sum of successful charge transactions for that day (in cents).
     *
     * @param  Request $request
     * @return array
     */
    public function chartData(Request $request)
    {
        $days = $this->validatePeriod($request->get('days'));

        $chartData = [];
        $labels = [];

        for ($i = $days - 1; $i >= 0; $i--) {
            $date = gmdate('Y-m-d', strtotime("-{$i} days"));
            $dateStart = $date . ' 00:00:00';
            $dateEnd = $date . ' 23:59:59';

            // Sum successful charge transactions for this day (raw cents)
            $revenueCents = (int) SubscriptionTransaction::where('status', SubscriptionTransaction::STATUS_SUCCESS)
                ->whereIn('transaction_type', self::CHARGE_TYPES)
                ->where('created_at', '>=', $dateStart)
                ->where('created_at', '<=', $dateEnd)
                ->sum('total');

            $chartData[] = $revenueCents;
            $labels[] = gmdate('M j', strtotime($date));
        }

        return [
            'success' => true,
            'data' => [
                'labels' => $labels,
                'data' => $chartData,
                'period' => $days,
            ]
        ];
    }

    /**
     * Get subscription status breakdown
     *
     * Returns counts for each subscription status + on_trial.
     *
     * @return array
     */
    public function subscriptionOverview()
    {
        $now = gmdate('Y-m-d H:i:s');

        $active = Subscription::where('status', Subscription::STATUS_ACTIVE)->count();
        $onTrial = Subscription::where('status', Subscription::STATUS_ACTIVE)
            ->whereNotNull('trial_ends_at')
            ->where('trial_ends_at', '>', $now)
            ->count();
        $paused = Subscription::where('status', Subscription::STATUS_PAUSED)->count();
        $canceled = Subscription::where('status', Subscription::STATUS_CANCELED)->count();
        $expired = Subscription::where('status', Subscription::STATUS_EXPIRED)->count();
        $pending = Subscription::where('status', Subscription::STATUS_PENDING)->count();

        return [
            'success' => true,
            'data' => [
                'active' => $active,
                'on_trial' => $onTrial,
                'paused' => $paused,
                'canceled' => $canceled,
                'expired' => $expired,
                'pending' => $pending,
                'total' => $active + $paused + $canceled + $expired + $pending,
            ]
        ];
    }

    /**
     * Get license usage / utilization stats
     *
     * Returns: active license count, total activations used vs total slots available,
     * utilization percentage, licenses at capacity, licenses expiring soon.
     *
     * @return array
     */
    public function licenseUsage()
    {
        $now = gmdate('Y-m-d H:i:s');
        $sevenDaysFromNow = gmdate('Y-m-d H:i:s', time() + (7 * DAY_IN_SECONDS));

        $activeLicenses = License::where('status', License::STATUS_ACTIVE)->count();

        // Total activations in use (across all active licenses)
        $totalActivations = (int) License::where('status', License::STATUS_ACTIVE)
            ->sum('activation_count');

        // Total activation slots available (only count licenses with a defined limit)
        $totalLimit = (int) License::where('status', License::STATUS_ACTIVE)
            ->whereNotNull('activation_limit')
            ->where('activation_limit', '>', 0)
            ->sum('activation_limit');

        // Licenses at full capacity (activation_count >= activation_limit)
        $atCapacity = License::where('status', License::STATUS_ACTIVE)
            ->whereNotNull('activation_limit')
            ->where('activation_limit', '>', 0)
            ->whereColumn('activation_count', '>=', 'activation_limit')
            ->count();

        // Licenses expiring within 7 days
        $expiringSoon = License::where('status', License::STATUS_ACTIVE)
            ->whereNotNull('expire_date')
            ->where('expire_date', '>', $now)
            ->where('expire_date', '<=', $sevenDaysFromNow)
            ->count();

        // Utilization: activations used / total slots (only for limited licenses)
        $utilization = $totalLimit > 0 ? round(($totalActivations / $totalLimit) * 100, 1) : 0;

        return [
            'success' => true,
            'data' => [
                'active_licenses' => $activeLicenses,
                'total_activations' => $totalActivations,
                'total_limit' => $totalLimit,
                'utilization' => $utilization,
                'at_capacity' => $atCapacity,
                'expiring_soon' => $expiringSoon,
            ]
        ];
    }

    /**
     * Get recent transactions (actual money movements)
     *
     * Returns last N transactions with amount, type, status, and subscription reference.
     *
     * @param  Request $request
     * @return array
     */
    public function recentTransactions(Request $request)
    {
        $limit = absint($request->get('limit') ?? 8);
        $limit = min($limit, 20);

        $transactions = SubscriptionTransaction::with([
                'customer:id,first_name,last_name',
                'subscription.subscriptionProducts:id,subscription_id,product_name',
            ])
            ->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();

        $result = [];

        foreach ($transactions as $txn) {
            $customerName = '';
            if ($txn->customer) {
                $customerName = trim($txn->customer->first_name . ' ' . $txn->customer->last_name);
            }

            $productName = '';
            if ($txn->subscription && $txn->subscription->subscriptionProducts->isNotEmpty()) {
                $names = $txn->subscription->subscriptionProducts->pluck('product_name')->filter()->values();
                $productName = $names->count() > 1
                    ? $names->first() . ' +' . ($names->count() - 1)
                    : $names->first() ?? '';
            }

            $result[] = [
                'id' => $txn->id,
                'total' => (int) round(($txn->total ?? 0) * 100), // Cents (MoneyCast returns dollars)
                'currency' => $txn->currency ?? 'USD',
                'transaction_type' => $txn->transaction_type,
                'status' => $txn->status,
                'subscription_id' => $txn->subscription_id,
                'customer_name' => $customerName ?: __('Unknown', 'licenzo'),
                'product_name' => $productName,
                'provider' => $txn->provider,
                'created_at' => $txn->created_at instanceof \DateTimeInterface
                    ? $txn->created_at->format('Y-m-d H:i:s')
                    : (string) $txn->created_at,
            ];
        }

        return [
            'success' => true,
            'data' => $result,
        ];
    }

    /**
     * Get recent activity feed (combined subscription + license events)
     *
     * @param  Request $request
     * @return array
     */
    public function recentActivity(Request $request)
    {
        $limit = absint($request->get('limit') ?? 10);
        $limit = min($limit, 20);

        // Subscription events (latest batch)
        $subEvents = SubscriptionEvent::orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();

        // License events (exclude high-frequency validation events)
        $licenseEvents = LicenseEvent::whereNotIn('event_type', [
                LicenseEvent::EVENT_VALIDATED,
                LicenseEvent::EVENT_VALIDATION_FAILED,
                LicenseEvent::EVENT_HASH_VALIDATED,
                LicenseEvent::EVENT_HASH_INVALID,
            ])
            ->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();

        // Merge, sort, limit
        $activities = [];

        foreach ($subEvents as $event) {
            $createdAt = $event->created_at instanceof \DateTimeInterface
                ? $event->created_at->format('Y-m-d H:i:s')
                : (string) $event->created_at;

            $activities[] = [
                'type' => 'subscription',
                'event' => $event->event_type,
                'source' => $event->event_source,
                'subscription_id' => $event->subscription_id,
                'customer_id' => $event->customer_id,
                'description' => $this->describeSubscriptionEvent($event),
                'created_at' => $createdAt,
            ];
        }

        foreach ($licenseEvents as $event) {
            $createdAt = $event->created_at instanceof \DateTimeInterface
                ? $event->created_at->format('Y-m-d H:i:s')
                : (string) $event->created_at;

            $activities[] = [
                'type' => 'license',
                'event' => $event->event_type,
                'source' => $event->event_source,
                'license_id' => $event->license_id,
                'product_name' => $event->product_name,
                'description' => $this->describeLicenseEvent($event),
                'created_at' => $createdAt,
            ];
        }

        usort($activities, function ($a, $b) {
            return strtotime($b['created_at']) - strtotime($a['created_at']);
        });

        return [
            'success' => true,
            'data' => array_slice($activities, 0, $limit),
        ];
    }

    /**
     * Get top products by active activation count
     *
     * @return array
     */
    public function topProducts()
    {
        $topProductIds = LicenseActivation::select('product_id')
            ->where('status', LicenseActivation::STATUS_ACTIVE)
            ->groupBy('product_id')
            ->orderByRaw('COUNT(*) DESC')
            ->limit(5)
            ->pluck('product_id')
            ->toArray();

        // Fill from Product table if < 5 results
        if (count($topProductIds) < 5) {
            $needed = 5 - count($topProductIds);
            $extra = Product::whereNotIn('id', $topProductIds)
                ->orderBy('created_at', 'desc')
                ->limit($needed)
                ->pluck('id')
                ->toArray();
            $topProductIds = array_merge($topProductIds, $extra);
        }

        $products = Product::whereIn('id', $topProductIds)
            ->get()
            ->keyBy('id');

        $activationCounts = [];
        if (!empty($topProductIds)) {
            $rows = LicenseActivation::select('product_id')
                ->selectRaw('COUNT(*) as count')
                ->where('status', LicenseActivation::STATUS_ACTIVE)
                ->whereIn('product_id', $topProductIds)
                ->groupBy('product_id')
                ->get();

            foreach ($rows as $row) {
                $activationCounts[$row->product_id] = (int) $row->count;
            }
        }

        $result = [];
        foreach ($topProductIds as $pid) {
            $product = $products->get($pid);
            if (!$product) {
                continue;
            }

            $result[] = [
                'id' => $product->id,
                'name' => $product->name,
                'slug' => $product->slug ?? null,
                'activations' => $activationCounts[$pid] ?? 0,
            ];
        }

        usort($result, function ($a, $b) {
            return $b['activations'] - $a['activations'];
        });

        return [
            'success' => true,
            'data' => $result,
        ];
    }

    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------

    /**
     * Calculate MRR (Monthly Recurring Revenue) in cents
     *
     * Iterates active subscriptions, normalizes recurring_amount by billing_cycle
     * to monthly equivalent. Returns integer cents.
     *
     * @return int MRR in cents
     */
    private function calculateMrr(): int
    {
        $activeSubscriptions = Subscription::where('status', Subscription::STATUS_ACTIVE)->get();

        $mrrDollars = 0.0;

        foreach ($activeSubscriptions as $sub) {
            $recurringAmount = $sub->recurring_amount ?? 0; // Dollars (via MoneyCast)
            $interval = $sub->billing_cycle;

            $mrrDollars += $this->normalizeToMonthly($recurringAmount, $interval);
        }

        return (int) round($mrrDollars * 100);
    }

    /**
     * Normalize a dollar amount to monthly equivalent based on billing interval
     *
     * @param float  $amount   Dollar amount
     * @param string|null $interval Billing interval constant
     * @return float Monthly dollar amount
     */
    private function normalizeToMonthly(float $amount, ?string $interval): float
    {
        return match ($interval) {
            Subscription::INTERVAL_MONTHLY => $amount,
            Subscription::INTERVAL_YEARLY  => $amount / 12,
            Subscription::INTERVAL_WEEKLY  => $amount * (52 / 12),
            Subscription::INTERVAL_DAILY   => $amount * (365 / 12),
            default => $amount, // Unknown interval: treat as monthly
        };
    }

    /**
     * Validate and return a period value from allowed list
     *
     * @param mixed $input
     * @return int
     */
    private function validatePeriod($input): int
    {
        $value = absint($input ?? 14);
        return in_array($value, self::ALLOWED_PERIODS, true) ? $value : 14;
    }



    /**
     * Generate human-readable description for a subscription event
     *
     * @param SubscriptionEvent $event
     * @return string
     */
    private function describeSubscriptionEvent(SubscriptionEvent $event): string
    {
        $map = [
            SubscriptionEvent::EVENT_SUBSCRIPTION_CREATED => 'New subscription created',
            SubscriptionEvent::EVENT_SUBSCRIPTION_ACTIVATED => 'Subscription activated',
            SubscriptionEvent::EVENT_SUBSCRIPTION_PAUSED => 'Subscription paused',
            SubscriptionEvent::EVENT_SUBSCRIPTION_RESUMED => 'Subscription resumed',
            SubscriptionEvent::EVENT_SUBSCRIPTION_CANCELED => 'Subscription canceled',
            SubscriptionEvent::EVENT_SUBSCRIPTION_EXPIRED => 'Subscription expired',
            SubscriptionEvent::EVENT_STATUS_CHANGED => 'Subscription status changed',
            SubscriptionEvent::EVENT_TRIAL_STARTED => 'Trial started',
            SubscriptionEvent::EVENT_TRIAL_CONVERTED => 'Trial converted to paid',
            SubscriptionEvent::EVENT_TRIAL_EXPIRED => 'Trial expired',
            SubscriptionEvent::EVENT_TRIAL_EXTENDED => 'Trial extended',
            SubscriptionEvent::EVENT_LICENSE_CREATED => 'License created for subscription',
            SubscriptionEvent::EVENT_LICENSE_ACTIVATED => 'License activated',
            SubscriptionEvent::EVENT_LICENSE_DEACTIVATED => 'License deactivated',
            SubscriptionEvent::EVENT_LICENSE_REVOKED => 'License revoked',
            SubscriptionEvent::EVENT_LICENSE_RENEWED => 'License renewed',
            SubscriptionEvent::EVENT_LICENSE_EXPIRED => 'License expired',
            SubscriptionEvent::EVENT_PRODUCT_UPGRADED => 'Plan upgraded',
            SubscriptionEvent::EVENT_PRODUCT_DOWNGRADED => 'Plan downgraded',
            SubscriptionEvent::EVENT_PRODUCT_CHANGED => 'Plan changed',
            SubscriptionEvent::EVENT_PAYMENT_SUCCEEDED => 'Payment succeeded',
            SubscriptionEvent::EVENT_PAYMENT_FAILED => 'Payment failed',
            SubscriptionEvent::EVENT_REFUND_ISSUED => 'Refund issued',
            SubscriptionEvent::EVENT_SCHEDULE_CREATED => 'Schedule created',
            SubscriptionEvent::EVENT_SCHEDULE_EXECUTED => 'Scheduled action executed',
            SubscriptionEvent::EVENT_SCHEDULE_CANCELED => 'Schedule canceled',
            SubscriptionEvent::EVENT_WEBHOOK_RECEIVED => 'Webhook received',
            SubscriptionEvent::EVENT_WEBHOOK_PROCESSED => 'Webhook processed',
        ];

        return $map[$event->event_type] ?? ucwords(str_replace('_', ' ', $event->event_type));
    }

    /**
     * Generate human-readable description for a license event
     *
     * @param LicenseEvent $event
     * @return string
     */
    private function describeLicenseEvent(LicenseEvent $event): string
    {
        $productLabel = $event->product_name ? " ({$event->product_name})" : '';

        $map = [
            LicenseEvent::EVENT_ACTIVATED => "License activated{$productLabel}",
            LicenseEvent::EVENT_DEACTIVATED => "License deactivated{$productLabel}",
            LicenseEvent::EVENT_EXPIRED => "License expired{$productLabel}",
            LicenseEvent::EVENT_REVOKED => "License revoked{$productLabel}",
            LicenseEvent::EVENT_RENEWED => "License renewed{$productLabel}",
            LicenseEvent::EVENT_UPGRADED => "License upgraded{$productLabel}",
            LicenseEvent::EVENT_DOWNGRADED => "License downgraded{$productLabel}",
            LicenseEvent::EVENT_SUSPENDED => "License suspended{$productLabel}",
            LicenseEvent::EVENT_REACTIVATED => "License reactivated{$productLabel}",
            LicenseEvent::EVENT_QUOTA_EXCEEDED => "Activation quota exceeded{$productLabel}",
            LicenseEvent::EVENT_SECURITY_ALERT => "Security alert{$productLabel}",
            LicenseEvent::EVENT_FRAUD_DETECTED => "Fraud detected{$productLabel}",
            LicenseEvent::EVENT_HASH_GENERATED => "Hash generated{$productLabel}",
            LicenseEvent::EVENT_INSTANCE_CHANGED => "Instance changed{$productLabel}",
        ];

        return $map[$event->event_type] ?? ucwords(str_replace('_', ' ', $event->event_type)) . $productLabel;
    }
}
