<?php

namespace App\Services\Report;

use App\DTO\ReportFilters;
use App\Models\Feature;
use App\Models\Inspection;
use App\Models\Issue;
use App\Models\Media;
use App\Models\Phase;
use App\Models\ProgressSnapshot;
use App\Models\Project;
use App\Models\Task;
use Carbon\Carbon;

class DataAggregator
{
    protected Project $project;
    protected ReportFilters $filters;

    public function __construct(Project $project, ReportFilters $filters)
    {
        $this->project = $project;
        $this->filters = $filters;
    }

    public function aggregate(): array
    {
        $data = [
            'project' => $this->project,
            'filters' => $this->filters,
            'generated_at' => now(),
            'generated_by' => auth()->guard()->user(),
        ];

        // Always include summary KPIs
        $data['summary'] = $this->buildSummary();

        // Include requested entity sections
        if (in_array('phases', $this->filters->entityTypes)) {
            $data['phases'] = $this->buildPhasesData();
        }

        if (in_array('features', $this->filters->entityTypes)) {
            $data['features'] = $this->buildFeaturesData();
        }

        if (in_array('inspections', $this->filters->entityTypes)) {
            $data['inspections'] = $this->buildInspectionsData();
        }

        if (in_array('issues', $this->filters->entityTypes)) {
            $data['issues'] = $this->buildIssuesData();
        }

        if (in_array('tasks', $this->filters->entityTypes)) {
            $data['tasks'] = $this->buildTasksData();
        }

        if (in_array('media', $this->filters->entityTypes)) {
            $data['media'] = $this->buildMediaData();
        }

        // Deviation analysis (always included if phases/features selected)
        if (in_array('phases', $this->filters->entityTypes) || in_array('features', $this->filters->entityTypes)) {
            $data['deviations'] = $this->buildDeviationsData();
        }

        // Progress curve data (for S-curve chart)
        if ($this->filters->includeCharts) {
            $data['progress_curve'] = $this->buildProgressCurveData();
        }

        return $data;
    }

    protected function buildSummary(): array
    {
        $phases = $this->project->phases()
            ->with(['layers.features'])
            ->get();

        $allFeatures = $phases->flatMap(fn ($p) => $p->layers->flatMap(fn ($l) => $l->features));
        $completedFeatures = $allFeatures->where('status', 'completed')->count();

        $inspectionsQuery = Inspection::where('project_id', $this->project->id);
        $this->applyDateFilter($inspectionsQuery, 'created_at');
        $totalInspections = $inspectionsQuery->count();
        $passedInspections = (clone $inspectionsQuery)->where('result', 'pass')->count();
        $failedInspections = (clone $inspectionsQuery)->where('result', 'fail')->count();

        $issuesQuery = Issue::where('project_id', $this->project->id);
        $this->applyDateFilter($issuesQuery, 'created_at');
        $openIssues = (clone $issuesQuery)->where('status', 'open')->count();
        $closedIssues = (clone $issuesQuery)->where('status', 'closed')->count();

        $tasksQuery = Task::where('project_id', $this->project->id);
        $this->applyDateFilter($tasksQuery, 'created_at');
        $totalTasks = $tasksQuery->count();
        $completedTasks = (clone $tasksQuery)->where('status', 'completed')->count();

        // Calculate overall planned vs actual progress
        $avgPlannedProgress = $phases->avg('planned_progress_at') ?? 0;
        $avgActualProgress = $phases->avg('progress_percent') ?? 0;
        $overallSpi = $avgPlannedProgress > 0 ? round($avgActualProgress / $avgPlannedProgress, 2) : null;

        return [
            'total_features' => $allFeatures->count(),
            'completed_features' => $completedFeatures,
            'completion_rate' => $allFeatures->count() > 0 ? round($completedFeatures / $allFeatures->count() * 100, 1) : 0,
            'avg_planned_progress' => round($avgPlannedProgress, 1),
            'avg_actual_progress' => round($avgActualProgress, 1),
            'overall_spi' => $overallSpi,
            'total_inspections' => $totalInspections,
            'passed_inspections' => $passedInspections,
            'failed_inspections' => $failedInspections,
            'pass_rate' => $totalInspections > 0 ? round($passedInspections / $totalInspections * 100, 1) : 0,
            'open_issues' => $openIssues,
            'closed_issues' => $closedIssues,
            'total_tasks' => $totalTasks,
            'completed_tasks' => $completedTasks,
            'task_completion_rate' => $totalTasks > 0 ? round($completedTasks / $totalTasks * 100, 1) : 0,
            'phases_on_track' => $phases->where('is_on_track', true)->count(),
            'phases_delayed' => $phases->where('is_on_track', false)->count(),
            'phases_no_data' => $phases->where('is_on_track', null)->count(),
        ];
    }

    protected function buildPhasesData(): array
    {
        $phases = $this->project->phases()
            ->with(['layers.features'])
            ->orderBy('order')
            ->get();

        return $phases->map(function ($phase) {
            $phaseFeatures = $phase->layers->flatMap(fn ($l) => $l->features);

            return [
                'id' => $phase->id,
                'name' => $phase->name,
                'description' => $phase->description,
                'order' => $phase->order,
                'color' => $phase->color,
                'planned_start' => $phase->planned_start?->format('d/m/Y'),
                'planned_end' => $phase->planned_end?->format('d/m/Y'),
                'actual_start' => $phase->actual_start?->format('d/m/Y'),
                'actual_end' => $phase->actual_end?->format('d/m/Y'),
                'baseline_start' => $phase->baseline_start?->format('d/m/Y'),
                'baseline_end' => $phase->baseline_end?->format('d/m/Y'),
                'progress_percent' => $phase->progress_percent,
                'planned_progress' => $phase->planned_progress_at,
                'deviation_days' => $phase->deviation_days,
                'start_deviation_days' => $phase->start_deviation_days,
                'spi' => $phase->spi,
                'is_on_track' => $phase->is_on_track,
                'features_count' => $phaseFeatures->count(),
                'completed_features' => $phaseFeatures->where('status', 'completed')->count(),
                'layers' => $phase->layers->map(fn ($l) => [
                    'id' => $l->id,
                    'name' => $l->name,
                    'features_count' => $l->features->count(),
                ])->toArray(),
            ];
        })->toArray();
    }

    protected function buildFeaturesData(): array
    {
        $query = Feature::whereHas('layer.phase', fn ($q) => $q->where('project_id', $this->project->id))
            ->with(['layer.phase', 'template', 'inspections', 'issues']);

        // Apply date filter on feature created_at if date range provided
        $this->applyDateFilter($query, 'features.created_at');

        $features = $query->orderBy('id')->get();

        return $features->map(function ($feature) {
            $lastInspection = $feature->inspections->sortByDesc('created_at')->first();

            return [
                'id' => $feature->id,
                'name' => $feature->name,
                'phase' => $feature->layer?->phase?->name ?? '—',
                'layer' => $feature->layer?->name ?? '—',
                'status' => $feature->status,
                'status_label' => $this->getFeatureStatusLabel($feature->status),
                'status_color' => $feature->status_color,
                'progress' => $feature->progress,
                'planned_progress' => $feature->planned_progress_at,
                'planned_start' => $feature->planned_start?->format('d/m/Y'),
                'planned_end' => $feature->planned_end?->format('d/m/Y'),
                'actual_start' => $feature->actual_start?->format('d/m/Y'),
                'actual_end' => $feature->actual_end?->format('d/m/Y'),
                'deviation_days' => $feature->deviation_days,
                'start_deviation_days' => $feature->start_deviation_days,
                'spi' => $feature->spi,
                'is_on_track' => $feature->is_on_track,
                'responsible' => $feature->responsible ?? $feature->responsibleUser?->name ?? '—',
                'template' => $feature->template?->name ?? '—',
                'last_inspection_date' => $lastInspection?->created_at?->format('d/m/Y'),
                'last_inspection_result' => $lastInspection?->result,
                'inspections_count' => $feature->inspections->count(),
                'open_issues_count' => $feature->issues->where('status', 'open')->count(),
            ];
        })->toArray();
    }

    protected function buildInspectionsData(): array
    {
        $query = Inspection::where('project_id', $this->project->id)
            ->with(['feature.layer.phase', 'template', 'user', 'media']);

        $this->applyDateFilter($query, 'created_at');

        $inspections = $query->orderByDesc('created_at')->get();

        return $inspections->map(function ($inspection) {
            return [
                'id' => $inspection->id,
                'feature' => $inspection->feature?->name ?? '—',
                'phase' => $inspection->feature?->layer?->phase?->name ?? '—',
                'template' => $inspection->template?->name ?? '—',
                'inspector' => $inspection->user?->name ?? '—',
                'date' => $inspection->created_at->format('d/m/Y H:i'),
                'status' => $inspection->status,
                'result' => $inspection->result,
                'result_label' => match ($inspection->result) {
                    'pass' => 'Aprobada',
                    'fail' => 'Fallida',
                    'conditional' => 'Condicional',
                    default => '—',
                },
                'notes' => $inspection->notes,
                'photos_count' => $inspection->media->count(),
                'data' => $inspection->data ?? [],
            ];
        })->toArray();
    }

    protected function buildIssuesData(): array
    {
        $query = Issue::where('project_id', $this->project->id)
            ->with(['feature.layer.phase', 'reporter', 'assignee', 'tasks']);

        $this->applyDateFilter($query, 'created_at');

        $issues = $query->orderByDesc('created_at')->get();

        return $issues->map(function ($issue) {
            return [
                'id' => $issue->id,
                'title' => $issue->title,
                'feature' => $issue->feature?->name ?? '—',
                'phase' => $issue->feature?->layer?->phase?->name ?? '—',
                'priority' => $issue->priority,
                'priority_label' => match ($issue->priority) {
                    'low' => 'Baja',
                    'medium' => 'Media',
                    'high' => 'Alta',
                    'critical' => 'Crítica',
                    default => ucfirst($issue->priority ?? ''),
                },
                'status' => $issue->status,
                'status_label' => match ($issue->status) {
                    'open' => 'Abierta',
                    'in_review' => 'En revisión',
                    'closed' => 'Cerrada',
                    default => ucfirst($issue->status ?? ''),
                },
                'reporter' => $issue->reporter?->name ?? '—',
                'assignee' => $issue->assignee?->name ?? '—',
                'created_at' => $issue->created_at->format('d/m/Y'),
                'closed_at' => $issue->closed_at?->format('d/m/Y'),
                'days_open' => $issue->created_at->diffInDays($issue->closed_at ?? now()),
                'tasks_total' => $issue->tasks->count(),
                'tasks_completed' => $issue->tasks->where('is_done', true)->count(),
            ];
        })->toArray();
    }

    protected function buildTasksData(): array
    {
        $query = Task::where('project_id', $this->project->id)
            ->with(['phase', 'assignee', 'creator', 'subtasks']);

        $this->applyDateFilter($query, 'created_at');

        $tasks = $query->whereNull('parent_task_id')->orderBy('order')->get();

        return $tasks->map(function ($task) {
            return [
                'id' => $task->id,
                'title' => $task->title,
                'phase' => $task->phase?->name ?? '—',
                'status' => $task->status,
                'status_label' => $task->status_label,
                'priority' => $task->priority,
                'priority_label' => $task->priority_label,
                'assignee' => $task->assignee?->name ?? '—',
                'creator' => $task->creator?->name ?? '—',
                'due_date' => $task->due_date?->format('d/m/Y'),
                'start_date' => $task->start_date?->format('d/m/Y'),
                'completed_at' => $task->completed_at?->format('d/m/Y'),
                'estimated_hours' => $task->estimated_hours,
                'actual_hours' => $task->actual_hours,
                'progress' => $task->progress,
                'is_overdue' => $task->is_overdue,
                'subtasks' => $task->subtasks->map(fn ($st) => [
                    'id' => $st->id,
                    'title' => $st->title,
                    'status' => $st->status,
                    'assignee' => $st->assignee?->name ?? '—',
                    'progress' => $st->progress,
                ])->toArray(),
            ];
        })->toArray();
    }

    protected function buildMediaData(): array
    {
        $query = Media::whereHas('mediable', function ($q) {
            $q->where(function ($sub) {
                $sub->where('mediable_type', Phase::class)
                    ->orWhere('mediable_type', Feature::class)
                    ->orWhere('mediable_type', Inspection::class)
                    ->orWhere('mediable_type', Issue::class)
                    ->orWhere('mediable_type', Task::class);
            });
        })->whereHas('mediable', function ($q) {
            // Filter by project through relationships
            // This is complex with polymorphic, so we filter in PHP for simplicity
        });

        $this->applyDateFilter($query, 'created_at');

        $media = $query->orderByDesc('created_at')->get();

        // Filter by project in PHP (polymorphic complexity)
        $filtered = $media->filter(function ($m) {
            $mediable = $m->mediable;
            if (! $mediable) {
                return false;
            }

            if ($mediable instanceof Phase) {
                return $mediable->project_id === $this->project->id;
            }
            if ($mediable instanceof Feature) {
                return $mediable->layer?->phase?->project_id === $this->project->id;
            }
            if ($mediable instanceof Inspection) {
                return $mediable->project_id === $this->project->id;
            }
            if ($mediable instanceof Issue) {
                return $mediable->project_id === $this->project->id;
            }
            if ($mediable instanceof Task) {
                return $mediable->project_id === $this->project->id;
            }

            return false;
        });

        return $filtered->map(function ($m) {
            return [
                'id' => $m->id,
                'name' => $m->name,
                'type' => $m->category,
                'entity' => class_basename($m->mediable_type),
                'entity_name' => $m->mediable?->name ?? $m->mediable?->title ?? '—',
                'size' => $m->file_size ? $this->formatBytes($m->file_size) : '—',
                'uploaded_by' => $m->uploadedBy?->name ?? '—',
                'uploaded_at' => $m->created_at->format('d/m/Y H:i'),
                'url' => $m->url,
            ];
        })->values()->toArray();
    }

    protected function buildDeviationsData(): array
    {
        $phases = $this->project->phases()
            ->with(['layers.features'])
            ->get();

        $phaseDeviations = $phases->map(function ($phase) {
            return [
                'id' => $phase->id,
                'name' => $phase->name,
                'planned_start' => $phase->planned_start?->format('d/m/Y'),
                'planned_end' => $phase->planned_end?->format('d/m/Y'),
                'actual_start' => $phase->actual_start?->format('d/m/Y'),
                'actual_end' => $phase->actual_end?->format('d/m/Y'),
                'start_deviation' => $phase->start_deviation_days,
                'end_deviation' => $phase->deviation_days,
                'planned_progress' => $phase->planned_progress_at,
                'actual_progress' => $phase->progress_percent,
                'progress_deviation' => round($phase->progress_percent - $phase->planned_progress_at, 1),
                'spi' => $phase->spi,
                'is_on_track' => $phase->is_on_track,
            ];
        })->filter(fn ($p) => $p['planned_start'] || $p['planned_end'])->toArray();

        $featureDeviations = Feature::whereHas('layer.phase', fn ($q) => $q->where('project_id', $this->project->id))
            ->whereNotNull('planned_end')
            ->with(['layer.phase'])
            ->get()
            ->map(function ($feature) {
                return [
                    'id' => $feature->id,
                    'name' => $feature->name,
                    'phase' => $feature->layer?->phase?->name ?? '—',
                    'planned_start' => $feature->planned_start?->format('d/m/Y'),
                    'planned_end' => $feature->planned_end?->format('d/m/Y'),
                    'actual_start' => $feature->actual_start?->format('d/m/Y'),
                    'actual_end' => $feature->actual_end?->format('d/m/Y'),
                    'start_deviation' => $feature->start_deviation_days,
                    'end_deviation' => $feature->deviation_days,
                    'planned_progress' => $feature->planned_progress_at,
                    'actual_progress' => $feature->progress,
                    'progress_deviation' => round($feature->progress - $feature->planned_progress_at, 1),
                    'spi' => $feature->spi,
                    'is_on_track' => $feature->is_on_track,
                    'responsible' => $feature->responsible ?? $feature->responsibleUser?->name ?? '—',
                ];
            })->toArray();

        return [
            'phases' => $phaseDeviations,
            'features' => $featureDeviations,
            'summary' => [
                'phases_delayed' => count(array_filter($phaseDeviations, fn ($p) => ($p['end_deviation'] ?? 0) > 0)),
                'phases_early' => count(array_filter($phaseDeviations, fn ($p) => ($p['end_deviation'] ?? 0) < 0)),
                'phases_on_time' => count(array_filter($phaseDeviations, fn ($p) => ($p['end_deviation'] ?? 0) === 0)),
                'features_delayed' => count(array_filter($featureDeviations, fn ($f) => ($f['end_deviation'] ?? 0) > 0)),
                'features_early' => count(array_filter($featureDeviations, fn ($f) => ($f['end_deviation'] ?? 0) < 0)),
                'features_on_time' => count(array_filter($featureDeviations, fn ($f) => ($f['end_deviation'] ?? 0) === 0)),
            ],
        ];
    }

    protected function buildProgressCurveData(): array
    {
        // Get snapshots for this project's phases and features
        $phaseIds = $this->project->phases->pluck('id')->toArray();
        $featureIds = Feature::whereHas('layer.phase', fn ($q) => $q->whereIn('phase_id', $phaseIds))
            ->pluck('id')->toArray();
        $taskIds = Task::where('project_id', $this->project->id)->pluck('id')->toArray();

        $from = $this->filters->dateFrom ?? now()->subMonths(6);
        $to = $this->filters->dateTo ?? now();

        $snapshots = ProgressSnapshot::whereIn('trackable_type', [Phase::class, Feature::class, Task::class])
            ->where(function ($q) use ($phaseIds, $featureIds, $taskIds) {
                $q->where(function ($sub) use ($phaseIds) {
                    $sub->where('trackable_type', Phase::class)
                        ->whereIn('trackable_id', $phaseIds);
                })->orWhere(function ($sub) use ($featureIds) {
                    $sub->where('trackable_type', Feature::class)
                        ->whereIn('trackable_id', $featureIds);
                })->orWhere(function ($sub) use ($taskIds) {
                    $sub->where('trackable_type', Task::class)
                        ->whereIn('trackable_id', $taskIds);
                });
            })
            ->whereBetween('snapshot_date', [$from, $to])
            ->orderBy('snapshot_date')
            ->get();

        // Group by date and calculate average progress
        $grouped = $snapshots->groupBy('snapshot_date');

        $dates = [];
        $planned = [];
        $actual = [];

        foreach ($grouped as $date => $snaps) {
            $dates[] = Carbon::parse($date)->format('d/m/Y');

            $phaseSnaps = $snaps->where('trackable_type', Phase::class);
            $featureSnaps = $snaps->where('trackable_type', Feature::class);
            $taskSnaps = $snaps->where('trackable_type', Task::class);

            $avgPlanned = 0;
            $avgActual = 0;
            $count = 0;

            foreach ($phaseSnaps as $snap) {
                $avgPlanned += $snap->metadata['planned_progress'] ?? 0;
                $avgActual += $snap->progress;
                $count++;
            }
            foreach ($featureSnaps as $snap) {
                $avgPlanned += $snap->metadata['planned_progress'] ?? 0;
                $avgActual += $snap->progress;
                $count++;
            }
            foreach ($taskSnaps as $snap) {
                $avgPlanned += $snap->progress; // Tasks don't have planned progress easily
                $avgActual += $snap->progress;
                $count++;
            }

            $dates[] = Carbon::parse($date)->format('d/m/Y');
            $planned[] = $count > 0 ? round($avgPlanned / $count, 1) : 0;
            $actual[] = $count > 0 ? round($avgActual / $count, 1) : 0;
        }

        return [
            'dates' => $dates,
            'planned' => $planned,
            'actual' => $actual,
        ];
    }

    protected function applyDateFilter($query, string $column): void
    {
        if ($this->filters->dateFrom) {
            $query->whereDate($column, '>=', $this->filters->dateFrom);
        }
        if ($this->filters->dateTo) {
            $query->whereDate($column, '<=', $this->filters->dateTo);
        }
    }

    protected function getFeatureStatusLabel(string $status): string
    {
        return match ($status) {
            'planned' => 'Planificado',
            'started' => 'Iniciado',
            'in_progress' => 'En progreso',
            'completed' => 'Completado',
            'verified' => 'Verificado',
            default => ucfirst($status),
        };
    }

    protected function formatBytes(int $bytes): string
    {
        if ($bytes >= 1048576) {
            return round($bytes / 1048576, 1).' MB';
        }
        if ($bytes >= 1024) {
            return round($bytes / 1024, 1).' KB';
        }
        return $bytes.' B';
    }
}