<?php

namespace Modules\Dashboard\Repositories;

use Modules\Dashboard\Repositories\Contracts\KJCDashboardRepositoryInterface;
use Modules\KJCAssetEntitlement\Entities\KJCAssetEntitlement;
use Modules\KJCAssetHolding\Entities\KJCAssetHolding;
use App\Models\KJCHistoricalEntitlement;
use App\Models\KJCHistoricalHoldingStatus;
use Modules\Unit\Entities\Unit;
use Modules\Formation\Entities\Formation;
use App\Services\VisibilityService;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class KJCDashboardRepository implements KJCDashboardRepositoryInterface
{
    public function __construct(
        protected VisibilityService $visibilityService
    ) {}
    /**
     * Get total KJC asset entitlement
     */
    public function getTotalEntitlement(array $filters = [], $user = null): int
    {
        $query = KJCAssetEntitlement::query();
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters, excludeDateFilter: true);
        
        return $query->sum('entitlement');
    }

    /**
     * Get total KJC asset holdings
     */
    public function getTotalHoldings(array $filters = [], $user = null): int
    {
        $query = KJCAssetEntitlement::query();
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters, excludeDateFilter: true);
        
        return $query->sum('holding');
    }

    /**
     * Get KJC asset holdings by status.
     * Does not filter by date so totals match full KJCAssetHolding counts (unit/government/formation filters still apply).
     */
    public function getHoldingsByStatus(array $filters = [], $user = null): array
    {
        $query = KJCAssetHolding::query();
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters, excludeDateFilter: true);
        
        return $query->selectRaw('status, COUNT(*) as count')
            ->groupBy('status')
            ->get()
            ->toArray();
    }

    /**
     * Get KJC asset holdings with pagination
     */
    public function getHoldingsPaginated(array $filters = [], int $perPage = 20, $user = null): array
    {
        $query = KJCAssetHolding::with(['unit', 'kjcCategory', 'kjcSubcategory', 'kjcModel']);
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters);
        
        $holdings = $query->paginate($perPage);
        
        return [
            'items' => $holdings->items(),
            'pagination' => [
                'current_page' => $holdings->currentPage(),
                'per_page' => $holdings->perPage(),
                'total' => $holdings->total(),
                'last_page' => $holdings->lastPage(),
                'from' => $holdings->firstItem(),
                'to' => $holdings->lastItem()
            ]
        ];
    }

    /**
     * Get KJC asset entitlements with pagination
     */
    public function getEntitlementsPaginated(array $filters = [], int $perPage = 20, $user = null): array
    {
        $query = KJCAssetEntitlement::with(['kjcSubcategory']);
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters);
        
        $entitlements = $query->paginate($perPage);
        
        return [
            'items' => $entitlements->items(),
            'pagination' => [
                'current_page' => $entitlements->currentPage(),
                'per_page' => $entitlements->perPage(),
                'total' => $entitlements->total(),
                'last_page' => $entitlements->lastPage(),
                'from' => $entitlements->firstItem(),
                'to' => $entitlements->lastItem()
            ]
        ];
    }

    /**
     * Get KJC asset holdings by specific status
     */
    public function getHoldingsBySpecificStatus(string $status, array $filters = [], $user = null): array
    {
        $query = KJCAssetHolding::with(['unit', 'kjcCategory', 'kjcSubcategory', 'kjcModel'])
            ->where('status', $status);

        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }

        $this->applyFilters($query, $filters);

        $holdings = $query->paginate(20);

        return [
            'items' => $holdings->items(),
            'pagination' => [
                'current_page' => $holdings->currentPage(),
                'per_page' => $holdings->perPage(),
                'total' => $holdings->total(),
                'last_page' => $holdings->lastPage(),
                'from' => $holdings->firstItem(),
                'to' => $holdings->lastItem()
            ]
        ];
    }

    /**
     * Get KJC asset details by status (paginated).
     * When status is '' or 'null', returns assets where status column is NULL (not yet assigned).
     */
    public function getAssetDetailsByStatus(string $status, array $filters = [], $user = null): array
    {
        $status = trim($status ?? '');

        $query = KJCAssetHolding::query()
            ->with(['kjcCategory', 'kjcSubcategory', 'kjcModel', 'unit'])
            ->select([
                'id',
                'uuid',
                'kjc_category_id',
                'kjc_subcategory_id',
                'kjc_model_id',
                'registration_number',
                'kewpa_registration_number',
                'status',
                'unit_id',
                'purchase_date',
                'receipt_date',
                'purchase_price',
                'contract_reference',
                'economic_year',
                'economic_distance',
                'note',
                'engine_number',
                'chassis_number',
                'created_at',
                'updated_at'
            ]);

        if ($status === '' || $status === 'null') {
            $query->whereNull('status');
        } else {
            $query->whereNotNull('status')
                ->where('status', '=', $status);
        }

        if ($user) {
            $query->visibleTo($user);
        }

        $this->applyFilters($query, $filters, excludeDateFilter: true);

        $perPage = isset($filters['per_page']) ? (int) $filters['per_page'] : 10;
        $page = isset($filters['page']) ? (int) $filters['page'] : null;
        $assets = $query->paginate($perPage, ['*'], 'page', $page);

        return [
            'data' => $assets->items(),
            'pagination' => [
                'current_page' => $assets->currentPage(),
                'per_page' => $assets->perPage(),
                'total' => $assets->total(),
                'last_page' => $assets->lastPage(),
                'from' => $assets->firstItem(),
                'to' => $assets->lastItem(),
                'has_more_pages' => $assets->hasMorePages()
            ]
        ];
    }

    /**
     * Get KJC asset holdings grouped by unit
     */
    public function getHoldingsByUnit(array $filters = [], $user = null): array
    {
        $query = KJCAssetHolding::query();
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters);
        
        return $query->join('units', 'kjc_asset_holdings.unit_id', '=', 'units.id')
            ->selectRaw('units.name as unit_name, COUNT(*) as count')
            ->groupBy('units.name')
            ->get()
            ->toArray();
    }

    /**
     * Get KJC asset holdings monthly trends
     */
    public function getMonthlyTrends(array $filters = [], $user = null): array
    {
        $dateRange = $this->getDateRange($filters);
        
        $query = KJCAssetHolding::query();
        
        // Apply visibility scoping if user is provided
        if ($user) {
            $query->visibleTo($user);
        }
        
        $this->applyFilters($query, $filters);
        
        return $query->selectRaw('
                DATE_FORMAT(created_at, "%Y-%m") as month,
                status,
                COUNT(*) as count
            ')
            ->whereBetween('created_at', [$dateRange['from'], $dateRange['to']])
            ->groupBy('month', 'status')
            ->orderBy('month')
            ->get()
            ->toArray();
    }

    /**
     * Get KJC historical metrics (KEUPAYAAN, KESIAGAAN, SERVISIBILITI) by month
     * 1. KEUPAYAAN (%) = (PEGANGAN/PERJAWATAN) * 100
     * 2. SIAPSIAGA or KESIAGAAN (%) = (BDG/perjawatan) * 100
     * 3. SERVISIBILITI (%) = (BDG/pegangan) * 100
     */
    public function getHistoricalMetricsByMonth(array $filters = [], $user = null): array
    {
        $dateRange = $this->getDateRange($filters);
        $currentMonth = Carbon::now()->format('Y-m');
        
        // Get entitlement and holding data by month from historical tables using Eloquent
        $entitlementQuery = KJCHistoricalEntitlement::query()
            ->selectRaw('
                TO_CHAR(kjc_historical_entitlement.date, \'YYYY-MM\') as month,
                SUM(kjc_historical_entitlement.entitlement) as total_entitlement,
                SUM(kjc_historical_entitlement.holding) as total_holding
            ')
            ->whereBetween('kjc_historical_entitlement.date', [$dateRange['from'], $dateRange['to']]);

        // Apply filters to historical entitlement query
        $this->applyHistoricalFiltersToEloquent($entitlementQuery, $filters);
        
        // Apply visibility scoping if user is provided (uses HasVisibility trait)
        if ($user) {
            $entitlementQuery->visibleTo($user);
        }

        $entitlementData = $entitlementQuery->groupBy('month')
            ->orderBy('month')
            ->get()
            ->keyBy('month');

        // Get BDG status count by month from historical tables using Eloquent
        $bdgQuery = KJCHistoricalHoldingStatus::query()
            ->selectRaw('
                TO_CHAR(kjc_historical_holding_status.date, \'YYYY-MM\') as month,
                COUNT(*) as bdg_count
            ')
            ->where('kjc_historical_holding_status.status', 'BDG')
            ->whereBetween('kjc_historical_holding_status.date', [$dateRange['from'], $dateRange['to']]);

        // Apply filters to historical holding status query
        $this->applyHistoricalFiltersToEloquent($bdgQuery, $filters);
        
        // Apply visibility scoping if user is provided (uses HasVisibility trait)
        if ($user) {
            $bdgQuery->visibleTo($user);
        }

        $bdgData = $bdgQuery->groupBy('month')
            ->orderBy('month')
            ->get()
            ->keyBy('month');

        // Get current month data from real-time tables if current month is in range
        $currentMonthEntitlement = 0;
        $currentMonthHolding = 0;
        $currentMonthBdgCount = 0;
        
        if ($this->isCurrentMonthInRange($currentMonth, $dateRange)) {
            // Get current month entitlement data (all entitlements, not filtered by updated_at)
            // This ensures we get real-time entitlement values regardless of when they were last updated
            $currentEntitlementQuery = KJCAssetEntitlement::query();
            if ($user) {
                $currentEntitlementQuery->visibleTo($user);
            }
            $this->applyCurrentMonthFilters($currentEntitlementQuery, $filters);
            
            $currentMonthEntitlement = $currentEntitlementQuery->sum('entitlement');
            
            // Calculate current month holdings from actual KJCAssetHolding records
            // This ensures we get real-time holding counts even if entitlement.holding is stale
            $currentHoldingQuery = KJCAssetHolding::query();
            if ($user) {
                $currentHoldingQuery->visibleTo($user);
            }
            $this->applyCurrentMonthFilters($currentHoldingQuery, $filters);
            
            $currentMonthHolding = $currentHoldingQuery->count();
            
            // Get current month BDG count (only holdings updated in current month)
            // This shows BDG holdings that were updated/changed status in the current month
            $currentBdgQuery = KJCAssetHolding::query()
                ->where('status', 'BDG')
                ->whereMonth('updated_at', Carbon::now()->month)
                ->whereYear('updated_at', Carbon::now()->year);
            if ($user) {
                $currentBdgQuery->visibleTo($user);
            }
            $this->applyCurrentMonthFilters($currentBdgQuery, $filters);
            
            $currentMonthBdgCount = $currentBdgQuery->count();
        }

        // Generate all months in the range
        $months = [];
        $current = $dateRange['from']->copy()->startOfMonth();
        $end = $dateRange['to']->copy()->endOfMonth();
        
        while ($current->lte($end)) {
            $monthKey = $current->format('Y-m');
            $months[] = $monthKey;
            $current->addMonth();
        }

        // Calculate metrics for each month
        $result = [];
        foreach ($months as $month) {
            $entitlement = $entitlementData->get($month);
            $bdg = $bdgData->get($month);
            
            // Use current month real-time data if this is the current month
            if ($month === $currentMonth && $this->isCurrentMonthInRange($currentMonth, $dateRange)) {
                $totalEntitlement = $currentMonthEntitlement;
                $totalHolding = $currentMonthHolding;
                $bdgCount = $currentMonthBdgCount;
            } else {
                $totalEntitlement = $entitlement ? $entitlement->total_entitlement : 0;
                $totalHolding = $entitlement ? $entitlement->total_holding : 0;
                $bdgCount = $bdg ? $bdg->bdg_count : 0;
            }
            
            // Calculate metrics
            $keupayaan = $totalEntitlement > 0 ? ($totalHolding / $totalEntitlement) * 100 : 0;
            $kesiagaan = $totalEntitlement > 0 ? ($bdgCount / $totalEntitlement) * 100 : 0;
            $servisibiliti = $totalHolding > 0 ? ($bdgCount / $totalHolding) * 100 : 0;
            
            $result[] = [
                'month' => $month,
                'keupayaan' => round($keupayaan, 2),
                'kesiagaan' => round($kesiagaan, 2),
                'servisibiliti' => round($servisibiliti, 2),
                'total_entitlement' => $totalEntitlement,
                'total_holding' => $totalHolding,
                'bdg_count' => $bdgCount
            ];
        }
        
        return $result;
    }

    /**
     * Apply common filters to query
     */
    private function applyFilters($query, array $filters = [], bool $excludeDateFilter = false): void
    {
        $model = $query->getModel();
        if ($model && method_exists($model, 'getTable')) {
            $schema = $model->getConnection()->getSchemaBuilder();
            $columns = $schema->getColumnListing($model->getTable());
            
            // Only apply created_at filter if the model has created_at column (skip when excludeDateFilter)
            if (!$excludeDateFilter && in_array('created_at', $columns)) {
                $dateRange = $this->getDateRange($filters);
                $query->whereBetween('created_at', [$dateRange['from'], $dateRange['to']]);
            }
            
            // Apply unit_id filter if model has unit_id column
            if (isset($filters['unit_id']) && $filters['unit_id']) {
                if (in_array('unit_id', $columns)) {
                    $query->where('unit_id', $filters['unit_id']);
                }
            }
            
            // Apply government_id filter if provided
            if (isset($filters['government_id']) && $filters['government_id']) {
                $unitIds = $this->getUnitsByGovernment($filters['government_id']);
                if (in_array('unit_id', $columns)) {
                    if (!empty($unitIds)) {
                        $query->whereIn('unit_id', $unitIds);
                    } else {
                        // No units found for this government - return empty result
                        $query->whereNull('id');
                    }
                }
            }
            
            // Apply camp_id filter
            if (isset($filters['camp_id']) && $filters['camp_id']) {
                $query->whereHas('unit', function ($q) use ($filters) {
                    $q->where('camp_id', $filters['camp_id']);
                });
            }
            
            // Apply formation_id filter
            if (isset($filters['formation_id']) && $filters['formation_id']) {
                // Get units under this formation
                $unitIds = Unit::where('formation_id', $filters['formation_id'])
                    ->pluck('id')
                    ->toArray();
                
                if (!empty($unitIds)) {
                    if (in_array('unit_id', $columns)) {
                        $query->whereIn('unit_id', $unitIds);
                    } else {
                        // Fallback to whereHas if no unit_id column
                        $query->whereHas('unit', function ($q) use ($filters) {
                            $q->where('formation_id', $filters['formation_id']);
                        });
                    }
                } else {
                    // No units found for this formation - return empty result
                    $query->whereNull('id');
                }
            }
        }
    }
    
    /**
     * Get units by government ID
     * Uses VisibilityService for consistent logic
     */
    private function getUnitsByGovernment(int $governmentId): array
    {
        $governmentUnit = Unit::where('government_id', $governmentId)->first();
        if (!$governmentUnit) {
            // If no direct unit found, try to get from formation
            $formation = Formation::where('government_id', $governmentId)->first();
            if ($formation) {
                $governmentUnit = Unit::where('formation_id', $formation->id)->first();
            }
        }
        
        if (!$governmentUnit) {
            return [];
        }
        
        return $this->visibilityService->getUnitsUnderGovernment($governmentUnit);
    }

    /**
     * Get date range for filtering
     */
    private function getDateRange(array $filters = []): array
    {
        $dateFrom = $filters['date_from'] ?? Carbon::now()->startOfYear();
        $dateTo = $filters['date_to'] ?? Carbon::now()->endOfYear();
        
        return [
            'from' => Carbon::parse($dateFrom)->startOfDay(),
            'to' => Carbon::parse($dateTo)->endOfDay()
        ];
    }

    /**
     * Apply filters to historical Eloquent queries
     * Handles government_id, formation_id, unit_id, and camp_id filters
     */
    private function applyHistoricalFiltersToEloquent($query, array $filters = []): void
    {
        $model = $query->getModel();
        $isHoldingStatus = $model instanceof KJCHistoricalHoldingStatus;
        
        // Apply unit_id filter
        if (isset($filters['unit_id']) && $filters['unit_id']) {
            if ($isHoldingStatus) {
                // For holding status, filter through the relationship
                $query->whereHas('kjcAssetHolding', function ($q) use ($filters) {
                    $q->where('unit_id', $filters['unit_id']);
                });
            } else {
                $query->where('unit_id', $filters['unit_id']);
            }
        }
        
        // Apply government_id filter
        if (isset($filters['government_id']) && $filters['government_id']) {
            $unitIds = $this->getUnitsByGovernment($filters['government_id']);
            if (!empty($unitIds)) {
                if ($isHoldingStatus) {
                    $query->whereHas('kjcAssetHolding', function ($q) use ($unitIds) {
                        $q->whereIn('unit_id', $unitIds);
                    });
                } else {
                    $query->whereIn('unit_id', $unitIds);
                }
            } else {
                // No units found for this government - return empty result
                $query->whereRaw('1 = 0');
            }
        }
        
        // Apply formation_id filter
        if (isset($filters['formation_id']) && $filters['formation_id']) {
            // Get units under this formation
            $unitIds = Unit::where('formation_id', $filters['formation_id'])
                ->pluck('id')
                ->toArray();
            
            if (!empty($unitIds)) {
                if ($isHoldingStatus) {
                    $query->whereHas('kjcAssetHolding', function ($q) use ($unitIds) {
                        $q->whereIn('unit_id', $unitIds);
                    });
                } else {
                    $query->whereIn('unit_id', $unitIds);
                }
            } else {
                // No units found for this formation - return empty result
                $query->whereRaw('1 = 0');
            }
        }
        
        // Apply camp_id filter
        if (isset($filters['camp_id']) && $filters['camp_id']) {
            if ($isHoldingStatus) {
                $query->whereHas('kjcAssetHolding.unit', function ($q) use ($filters) {
                    $q->where('camp_id', $filters['camp_id']);
                });
            } else {
                $query->whereHas('unit', function ($q) use ($filters) {
                    $q->where('camp_id', $filters['camp_id']);
                });
            }
        }
    }

    /**
     * Check if current month is within the date range
     */
    private function isCurrentMonthInRange(string $currentMonth, array $dateRange): bool
    {
        $currentMonthDate = Carbon::createFromFormat('Y-m', $currentMonth)->startOfMonth();
        return $currentMonthDate->between($dateRange['from'], $dateRange['to']);
    }

    /**
     * Apply filters for current month data queries (without date range filtering)
     */
    private function applyCurrentMonthFilters($query, array $filters = []): void
    {
        if (isset($filters['unit_id']) && $filters['unit_id']) {
            $query->where('unit_id', $filters['unit_id']);
        }
        
        // Apply government_id filter
        if (isset($filters['government_id']) && $filters['government_id']) {
            $unitIds = $this->getUnitsByGovernment($filters['government_id']);
            if (!empty($unitIds)) {
                $query->whereIn('unit_id', $unitIds);
            } else {
                // No units found for this government - return empty result
                $query->whereNull('id');
            }
        }
        
        if (isset($filters['camp_id']) && $filters['camp_id']) {
            $query->whereHas('unit', function ($q) use ($filters) {
                $q->where('camp_id', $filters['camp_id']);
            });
        }
        
        if (isset($filters['formation_id']) && $filters['formation_id']) {
            // Get units under this formation
            $unitIds = Unit::where('formation_id', $filters['formation_id'])
                ->pluck('id')
                ->toArray();
            
            if (!empty($unitIds)) {
                $query->whereIn('unit_id', $unitIds);
            } else {
                // No units found for this formation - return empty result
                $query->whereNull('id');
            }
        }
    }

}
