<?php

namespace Modules\Dashboard\Services;

use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Modules\Unit\Entities\Unit;
use Modules\Camp\Entities\Camp;
use Modules\Formation\Entities\Formation;
use App\Services\VisibilityService;

abstract class DashboardService
{
    public function __construct(
        protected VisibilityService $visibilityService
    ) {}
    /**
     * Get date range for filtering
     */
    protected 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 common filters to query
     */
    protected function applyCommonFilters($query, array $filters = []): void
    {
        $dateRange = $this->getDateRange($filters);
        
        $query->whereBetween('created_at', [$dateRange['from'], $dateRange['to']]);
        
        // Government filter: Filter by government (get all units under that government)
        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
                $query->whereNull('id');
            }
        }
        
        if (isset($filters['unit_id']) && $filters['unit_id']) {
            $query->where('unit_id', $filters['unit_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']) {
            $query->whereHas('unit', function ($q) use ($filters) {
                $q->where('formation_id', $filters['formation_id']);
            });
        }
    }

    /**
     * Apply visibility scoping to query based on user permissions
     */
    protected function applyVisibilityScoping($query, $user): void
    {
        if (!$user) {
            return;
        }

        // Check if the model has the HasVisibility trait
        if (method_exists($query->getModel(), 'scopeVisibleTo')) {
            $query->visibleTo($user);
        }
    }

    /**
     * Apply visibility scoping to raw database queries
     * Uses VisibilityService for consistent logic
     */
    protected function applyRawVisibilityScoping($query, $user, string $tableName): void
    {
        if (!$user) {
            return;
        }

        $userUnit = Unit::find($user->unit_id);
        if (!$userUnit) {
            $query->whereRaw('1 = 0');
            return;
        }

        $unitIds = $this->visibilityService->getVisibleUnitIds($user);
        
        if (empty($unitIds)) {
            $query->whereRaw('1 = 0');
        } else {
            $query->whereIn('unit_id', $unitIds);
        }
    }

    /**
     * Get all units by government ID
     * Uses VisibilityService for consistent logic
     */
    protected 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 total units count
     */
    public function getTotalUnits(array $filters = []): int
    {
        $query = Unit::query();
        
        if (isset($filters['camp_id']) && $filters['camp_id']) {
            $query->where('camp_id', $filters['camp_id']);
        }
        
        if (isset($filters['formation_id']) && $filters['formation_id']) {
            $query->where('formation_id', $filters['formation_id']);
        }
        
        return $query->count();
    }

    /**
     * Get total camps count
     */
    public function getTotalCamps(array $filters = []): int
    {
        return Camp::count();
    }

    /**
     * Get total formations count
     */
    public function getTotalFormations(array $filters = []): int
    {
        return Formation::count();
    }

    /**
     * Get active repairs count (to be implemented by child classes)
     */
    abstract public function getActiveRepairs(array $filters = []): int;

    /**
     * Get pending reports count (to be implemented by child classes)
     */
    abstract public function getPendingReports(array $filters = []): int;

    /**
     * Get total assets count (to be implemented by child classes)
     */
    abstract public function getTotalAssets(array $filters = []): int;

    /**
     * Get operational assets count (to be implemented by child classes)
     */
    abstract public function getOperationalAssets(array $filters = []): int;

    /**
     * Get under repair assets count (to be implemented by child classes)
     */
    abstract public function getUnderRepairAssets(array $filters = []): int;

    /**
     * Get maintenance due assets count (to be implemented by child classes)
     */
    abstract public function getMaintenanceDueAssets(array $filters = []): int;

    /**
     * Format chart data with common structure
     */
    protected function formatChartData(
        string $chartType,
        string $title,
        array $series,
        array $categories = [],
        array $drillDown = []
    ): array {
        return [
            'chart_type' => $chartType,
            'title' => $title,
            'series' => $series,
            'categories' => $categories,
            'drill_down' => $drillDown
        ];
    }

    /**
     * Format pie chart data
     */
    protected function formatPieChartData(
        string $title,
        array $data,
        string $drillDownEndpoint = '',
        array $drillDownParameters = []
    ): array {
        $total = array_sum(array_column($data, 'value'));
        $series = [];
        
        foreach ($data as $item) {
            $percentage = $total > 0 ? round(($item['value'] / $total) * 100, 1) : 0;
            
            $seriesItem = [
                'name' => $item['name'],
                'value' => $item['value'],
                'percentage' => $percentage
            ];
            
            // Preserve status_code if it exists (for drill-down functionality)
            if (isset($item['status_code'])) {
                $seriesItem['status_code'] = $item['status_code'];
            }
            
            $series[] = $seriesItem;
        }
        
        return $this->formatChartData(
            'pie',
            $title,
            $series,
            [],
            [
                'enabled' => !empty($drillDownEndpoint),
                'endpoint' => $drillDownEndpoint,
                'parameters' => $drillDownParameters
            ]
        );
    }

    /**
     * Format bar chart data
     */
    protected function formatBarChartData(
        string $title,
        array $series,
        array $categories,
        string $drillDownEndpoint = '',
        array $drillDownParameters = []
    ): array {
        return $this->formatChartData(
            'bar',
            $title,
            $series,
            $categories,
            [
                'enabled' => !empty($drillDownEndpoint),
                'endpoint' => $drillDownEndpoint,
                'parameters' => $drillDownParameters
            ]
        );
    }

    /**
     * Format line chart data
     */
    protected function formatLineChartData(
        string $title,
        array $series,
        array $categories,
        string $drillDownEndpoint = '',
        array $drillDownParameters = []
    ): array {
        return $this->formatChartData(
            'line',
            $title,
            $series,
            $categories,
            [
                'enabled' => !empty($drillDownEndpoint),
                'endpoint' => $drillDownEndpoint,
                'parameters' => $drillDownParameters
            ]
        );
    }

    /**
     * Get monthly categories for date range
     */
    protected function getMonthlyCategories(array $filters = []): array
    {
        $dateRange = $this->getDateRange($filters);
        $categories = [];
        
        $current = $dateRange['from']->copy()->startOfMonth();
        $end = $dateRange['to']->copy()->endOfMonth();
        
        while ($current->lte($end)) {
            $categories[] = $current->format('M Y');
            $current->addMonth();
        }
        
        return $categories;
    }

    /**
     * Get weekly categories for date range
     */
    protected function getWeeklyCategories(array $filters = []): array
    {
        $dateRange = $this->getDateRange($filters);
        $categories = [];
        
        $current = $dateRange['from']->copy()->startOfWeek();
        $end = $dateRange['to']->copy()->endOfWeek();
        
        while ($current->lte($end)) {
            $categories[] = 'Week ' . $current->weekOfYear . ' ' . $current->year;
            $current->addWeek();
        }
        
        return $categories;
    }

    /**
     * Get daily categories for date range
     */
    protected function getDailyCategories(array $filters = []): array
    {
        $dateRange = $this->getDateRange($filters);
        $categories = [];
        
        $current = $dateRange['from']->copy();
        $end = $dateRange['to']->copy();
        
        while ($current->lte($end)) {
            $categories[] = $current->format('M d');
            $current->addDay();
        }
        
        return $categories;
    }

    /**
     * Calculate percentage change
     */
    protected function calculatePercentageChange(int $current, int $previous): float
    {
        if ($previous === 0) {
            return $current > 0 ? 100.0 : 0.0;
        }
        
        return round((($current - $previous) / $previous) * 100, 1);
    }

    /**
     * Get status color mapping
     */
    protected function getStatusColors(): array
    {
        return [
            'operational' => '#4caf50',
            'under_repair' => '#ff9800',
            'maintenance_due' => '#f44336',
            'out_of_service' => '#9e9e9e',
            'completed' => '#4caf50',
            'pending' => '#ff9800',
            'in_progress' => '#2196f3',
            'cancelled' => '#f44336'
        ];
    }

    /**
     * Get priority color mapping
     */
    protected function getPriorityColors(): array
    {
        return [
            'high' => '#f44336',
            'medium' => '#ff9800',
            'low' => '#4caf50'
        ];
    }

    /**
     * Format currency
     */
    protected function formatCurrency(float $amount): string
    {
        return 'RM ' . number_format($amount, 2);
    }

    /**
     * Format duration in hours
     */
    protected function formatDuration(float $hours): string
    {
        if ($hours < 24) {
            return round($hours, 1) . ' hours';
        }
        
        $days = floor($hours / 24);
        $remainingHours = $hours % 24;
        
        if ($remainingHours > 0) {
            return $days . ' days ' . round($remainingHours, 1) . ' hours';
        }
        
        return $days . ' days';
    }

    /**
     * Get common drill-down parameters
     */
    protected function getCommonDrillDownParameters(): array
    {
        return ['date_from', 'date_to', 'unit_id', 'camp_id', 'formation_id'];
    }

    /**
     * Build drill-down endpoint
     */
    protected function buildDrillDownEndpoint(string $baseEndpoint, array $parameters = []): string
    {
        $endpoint = $baseEndpoint;
        
        if (!empty($parameters)) {
            $endpoint .= '?' . http_build_query($parameters);
        }
        
        return $endpoint;
    }

    /**
     * Get summary statistics for drill-down
     */
    protected function getSummaryStatistics($query, array $filters = []): array
    {
        $total = $query->count();
        
        // Get breakdown by unit
        $byUnit = $query->clone()
            ->join('units', function ($join) {
                $join->on('units.id', '=', $this->getUnitIdColumn());
            })
            ->selectRaw('units.name as unit_name, COUNT(*) as count')
            ->groupBy('units.name')
            ->get()
            ->toArray();
        
        return [
            'total' => $total,
            'by_unit' => $byUnit
        ];
    }

    /**
     * Get unit ID column name (to be implemented by child classes)
     */
    abstract protected function getUnitIdColumn(): string;

    /**
     * Get asset model class (to be implemented by child classes)
     */
    abstract protected function getAssetModelClass(): string;

    /**
     * Get repair model class (to be implemented by child classes)
     */
    abstract protected function getRepairModelClass(): string;
}