<?php

namespace App\Services;

use App\Models\Thema;
use App\Models\Project;
use App\Models\Afhankelijkheid;
use Illuminate\Support\Str;

class MapDataService
{
    /**
     * Grid unit — all node positions snap to multiples of this value.
     * Keep in sync with resources/js/Components/MetroMap/gridConstants.js
     */
    private const GRID = 50;
    private const GRID_STEP_X = 200;   // horizontal spacing between nodes
    private const GRID_STEP_Y = 150;   // vertical spacing between metro lines

    private function snapToGrid(float $value): int
    {
        return (int) (round($value / self::GRID) * self::GRID);
    }

    /**
     * Build the Level 1 (Strategy) metro map data.
     * Each theme = a metro line, each project = a station.
     */
    public function getStrategyMap(): array
    {
        $themas = Thema::with([
            'speerpunten.projects' => function ($q) {
                $q->with(['eigenaar', 'fases', 'commitments.eigenaar', 'documents.auteur', 'risicos'])
                    ->withCount(['documents', 'commitments', 'risicos', 'fases']);
            }
        ])->get();

        $lines = [];
        $nodes = [];
        $connections = [];

        $lineColors = ['#00d2ff', '#e94560', '#00ff88', '#7b68ee', '#ff6b6b', '#ffd93d', '#6bcb77', '#4d96ff'];
        $yOffset = 0;

        foreach ($themas as $index => $thema) {
            $color = $lineColors[$index % count($lineColors)];
            $lines[] = [
                'id' => "thema-{$thema->id}",
                'name' => $thema->naam,
                'color' => $color,
            ];

            $projects = $thema->speerpunten->flatMap->projects;
            $xStart = $this->snapToGrid(-200);

            foreach ($projects as $order => $project) {
                $nodes[] = [
                    'id' => "project-{$project->id}",
                    'entityId' => $project->id,
                    'entityType' => 'project',
                    'name' => $project->naam,
                    'lineId' => "thema-{$thema->id}",
                    'x' => $this->snapToGrid($xStart + ($order * self::GRID_STEP_X)),
                    'y' => $this->snapToGrid($yOffset),
                    'order' => $order + 1,
                    'status' => $project->status->value,
                    'description' => Str::limit($project->beschrijving, 100),
                    'owner' => $project->eigenaar?->name,
                    'badge' => ucfirst(str_replace('_', ' ', $project->status->value)),
                    'children' => $this->buildProjectChildren($project),
                ];
            }

            $yOffset += self::GRID_STEP_Y;
        }

        // Get dependencies as connections
        $dependencies = Afhankelijkheid::all();
        foreach ($dependencies as $dep) {
            $connections[] = [
                'from' => "project-{$dep->project_id}",
                'to' => "project-{$dep->afhankelijk_van_project_id}",
            ];
        }

        return [
            'lines' => $lines,
            'nodes' => $nodes,
            'connections' => $connections,
            'level' => 1,
        ];
    }

    /**
     * Build Level 2 (Project) metro map data.
     * The project's lifecycle phases = a metro line, milestones = stations.
     */
    public function getProjectMap(int $projectId): array
    {
        $project = Project::with([
            'fases',
            'commitments' => fn ($q) => $q->with('eigenaar'),
            'documents',
            'risicos',
            'besluiten',
        ])->findOrFail($projectId);

        $lines = [
            ['id' => 'lifecycle', 'name' => $project->naam, 'color' => '#00d2ff'],
            ['id' => 'commitments', 'name' => 'Commitments', 'color' => '#e94560'],
            ['id' => 'documents', 'name' => 'Documenten', 'color' => '#7b68ee'],
        ];

        $nodes = [];
        $xStart = $this->snapToGrid(-300);

        // Phase nodes on lifecycle line
        foreach ($project->fases->sortBy('type') as $order => $fase) {
            $nodes[] = [
                'id' => "fase-{$fase->id}",
                'entityId' => $fase->id,
                'entityType' => 'fase',
                'name' => ucfirst(str_replace('_', ' ', $fase->type->value)),
                'lineId' => 'lifecycle',
                'x' => $this->snapToGrid($xStart + ($order * self::GRID_STEP_X)),
                'y' => $this->snapToGrid(-50),
                'order' => $order + 1,
                'status' => $fase->status->value,
                'badge' => ucfirst($fase->status->value),
                'children' => null,
            ];
        }

        // Commitment nodes
        foreach ($project->commitments as $order => $commitment) {
            $nodes[] = [
                'id' => "commitment-{$commitment->id}",
                'entityId' => $commitment->id,
                'entityType' => 'commitment',
                'name' => Str::limit($commitment->beschrijving, 40),
                'lineId' => 'commitments',
                'x' => $this->snapToGrid($xStart + ($order * self::GRID_STEP_X)),
                'y' => $this->snapToGrid(100),
                'order' => $order + 1,
                'status' => $commitment->status->value,
                'owner' => $commitment->eigenaar?->name,
                'badge' => $commitment->deadline?->format('d M'),
                'children' => null,
            ];
        }

        // Document nodes
        foreach ($project->documents as $order => $doc) {
            $nodes[] = [
                'id' => "document-{$doc->id}",
                'entityId' => $doc->id,
                'entityType' => 'document',
                'name' => $doc->titel,
                'lineId' => 'documents',
                'x' => $this->snapToGrid($xStart + ($order * self::GRID_STEP_X)),
                'y' => $this->snapToGrid(250),
                'order' => $order + 1,
                'status' => 'active',
                'badge' => "v{$doc->versie}",
                'children' => null,
            ];
        }

        return [
            'lines' => $lines,
            'nodes' => $nodes,
            'connections' => [],
            'level' => 2,
            'project' => [
                'id' => $project->id,
                'naam' => $project->naam,
                'status' => $project->status->value,
            ],
        ];
    }

    /**
     * Build the Level 2 children data for a project node.
     * Used inline in getStrategyMap() and via getNodeChildren().
     */
    private function buildProjectChildren(Project $project): array
    {
        $project->loadMissing(['fases', 'commitments.eigenaar', 'documents.auteur', 'risicos']);

        $lines = [
            ['id' => "lifecycle-{$project->id}", 'name' => 'Levenscyclus', 'color' => '#00d2ff'],
            ['id' => "commitments-{$project->id}", 'name' => 'Commitments', 'color' => '#e94560'],
            ['id' => "documents-{$project->id}", 'name' => 'Documenten', 'color' => '#7b68ee'],
        ];

        $nodes = [];
        $xStart = $this->snapToGrid(-250);

        // Phase nodes
        foreach ($project->fases->sortBy('created_at') as $i => $fase) {
            $nodes[] = [
                'id' => "fase-{$fase->id}",
                'entityId' => $fase->id,
                'entityType' => 'fase',
                'name' => ucfirst(str_replace('_', ' ', $fase->type->value)),
                'lineId' => "lifecycle-{$project->id}",
                'x' => $this->snapToGrid($xStart + ($i * self::GRID_STEP_X)),
                'y' => $this->snapToGrid(-50),
                'order' => $i + 1,
                'status' => $fase->status->value,
                'badge' => ucfirst($fase->status->value),
                'children' => null,
            ];
        }

        // Commitment nodes
        foreach ($project->commitments as $i => $commitment) {
            $nodes[] = [
                'id' => "commitment-{$commitment->id}",
                'entityId' => $commitment->id,
                'entityType' => 'commitment',
                'name' => Str::limit($commitment->beschrijving, 35),
                'lineId' => "commitments-{$project->id}",
                'x' => $this->snapToGrid($xStart + ($i * self::GRID_STEP_X)),
                'y' => $this->snapToGrid(100),
                'order' => $i + 1,
                'status' => $commitment->status->value,
                'owner' => $commitment->eigenaar?->name,
                'badge' => $commitment->deadline?->format('d M Y'),
                'children' => null,
            ];
        }

        // Document nodes
        foreach ($project->documents as $i => $doc) {
            $nodes[] = [
                'id' => "document-{$doc->id}",
                'entityId' => $doc->id,
                'entityType' => 'document',
                'name' => $doc->titel,
                'lineId' => "documents-{$project->id}",
                'x' => $this->snapToGrid($xStart + ($i * self::GRID_STEP_X)),
                'y' => $this->snapToGrid(250),
                'order' => $i + 1,
                'status' => 'active',
                'badge' => "v{$doc->versie}",
                'children' => null,
            ];
        }

        return [
            'lines' => $lines,
            'nodes' => $nodes,
            'connections' => [],
            'parentEntityType' => 'project',
            'parentEntityId' => $project->id,
            'parentName' => $project->naam,
        ];
    }

    /**
     * Return children for any node by entity type and ID.
     */
    public function getNodeChildren(string $entityType, int $entityId): ?array
    {
        return match ($entityType) {
            'project' => $this->buildProjectChildren(Project::findOrFail($entityId)),
            default => null,
        };
    }
}
