<?php

namespace App\Services;

use App\Models\Organization;
use App\Models\LeadershipPosition;
use App\Models\LeadershipAssignment;
use Illuminate\Support\Collection;

/**
 * OrganogramService
 *
 * Service class for building organizational hierarchy from leadership positions
 * and assignments. Supports exporting to various formats (PDF, PNG, SVG).
 */
class OrganogramService
{
    /**
     * Build hierarchical tree structure from leadership positions and assignments.
     *
     * @param Organization $organization
     * @return array
     */
    public function buildHierarchy(Organization $organization): array
    {
        // Get root positions (positions without parent) with eager loading
        $positions = $organization->leadershipPositions()
            ->with(['currentAssignment.user', 'childPositions'])
            ->whereNull('parent_position_id')
            ->orderBy('hierarchy_level')
            ->get();

        return $positions->map(function ($position) {
            return $this->buildNode($position);
        })->toArray();
    }

    /**
     * Recursively build tree node from a leadership position.
     *
     * @param LeadershipPosition $position
     * @return array
     */
    public function buildNode(LeadershipPosition $position): array
    {
        $node = [
            'id' => $position->id,
            'title' => $position->title,
            'hierarchy_level' => $position->hierarchy_level,
            'hierarchy_level_label' => $position->hierarchy_level_label,
            'description' => $position->description,
            'assignee' => $position->currentAssignment?->user?->name,
            'assignee_id' => $position->currentAssignment?->user_id,
            'start_date' => $position->currentAssignment?->start_date?->format('Y-m-d'),
            'children' => [],
        ];

        // Recursively build children
        $children = $position->childPositions()
            ->with(['currentAssignment.user', 'childPositions'])
            ->orderBy('hierarchy_level')
            ->get();

        foreach ($children as $child) {
            $node['children'][] = $this->buildNode($child);
        }

        return $node;
    }

    /**
     * Build flat list of all positions with their assignments.
     *
     * @param Organization $organization
     * @return Collection
     */
    public function buildFlatList(Organization $organization): Collection
    {
        return $organization->leadershipPositions()
            ->with(['currentAssignment.user', 'parentPosition'])
            ->orderBy('hierarchy_level')
            ->orderBy('title')
            ->get()
            ->map(function ($position) {
                return [
                    'id' => $position->id,
                    'title' => $position->title,
                    'hierarchy_level' => $position->hierarchy_level,
                    'hierarchy_level_label' => $position->hierarchy_level_label,
                    'parent_title' => $position->parentPosition?->title,
                    'assignee' => $position->currentAssignment?->user?->name,
                    'depth' => count($position->getHierarchyPath()) - 1,
                ];
            });
    }

    /**
     * Get statistics about the organizational hierarchy.
     *
     * @param Organization $organization
     * @return array
     */
    public function getStatistics(Organization $organization): array
    {
        $positions = $organization->leadershipPositions()->with('currentAssignment')->get();

        $totalPositions = $positions->count();
        $filledPositions = $positions->filter(fn ($p) => $p->currentAssignment)->count();
        $emptyPositions = $totalPositions - $filledPositions;

        $byLevel = $positions->groupBy('hierarchy_level')->map->count();

        return [
            'total_positions' => $totalPositions,
            'filled_positions' => $filledPositions,
            'empty_positions' => $emptyPositions,
            'fill_rate' => $totalPositions > 0 ? round(($filledPositions / $totalPositions) * 100, 1) : 0,
            'by_level' => $byLevel,
        ];
    }

    /**
     * Export organogram to specified format.
     *
     * @param Organization $organization
     * @param string $format
     * @return mixed
     * @throws \InvalidArgumentException
     */
    public function export(Organization $organization, string $format): mixed
    {
        $hierarchy = $this->buildHierarchy($organization);
        $statistics = $this->getStatistics($organization);

        return match ($format) {
            'pdf' => $this->exportToPdf($organization, $hierarchy, $statistics),
            'png' => $this->exportToPng($organization, $hierarchy, $statistics),
            'svg' => $this->exportToSvg($organization, $hierarchy, $statistics),
            'json' => $this->exportToJson($organization, $hierarchy, $statistics),
            default => throw new \InvalidArgumentException("Unsupported export format: {$format}")
        };
    }

    /**
     * Export organogram to PDF format.
     *
     * @param Organization $organization
     * @param array $hierarchy
     * @param array $statistics
     * @return string Path to PDF file
     */
    protected function exportToPdf(Organization $organization, array $hierarchy, array $statistics): string
    {
        // Generate SVG content for PDF
        $svgContent = $this->generateSvgContent($hierarchy, $organization->name);

        // For production, you would use a library like DomPDF, Snappy, or wkhtmltopdf
        // This is a placeholder that returns the SVG wrapped in HTML for PDF conversion
        $html = view('organization.organogram.export-pdf', [
            'organization' => $organization,
            'hierarchy' => $hierarchy,
            'statistics' => $statistics,
            'svgContent' => $svgContent,
        ])->render();

        // Store temporarily
        $filename = 'organogram_' . $organization->id . '_' . now()->format('YmdHis') . '.html';
        $path = storage_path('app/exports/' . $filename);

        if (!is_dir(dirname($path))) {
            mkdir(dirname($path), 0755, true);
        }

        file_put_contents($path, $html);

        return $path;
    }

    /**
     * Export organogram to PNG format.
     *
     * @param Organization $organization
     * @param array $hierarchy
     * @param array $statistics
     * @return string Path to PNG file
     */
    protected function exportToPng(Organization $organization, array $hierarchy, array $statistics): string
    {
        // For production, you would use a library like Intervention Image or similar
        // This generates an SVG that can be converted to PNG
        $svgContent = $this->generateSvgContent($hierarchy, $organization->name);

        $filename = 'organogram_' . $organization->id . '_' . now()->format('YmdHis') . '.svg';
        $path = storage_path('app/exports/' . $filename);

        if (!is_dir(dirname($path))) {
            mkdir(dirname($path), 0755, true);
        }

        file_put_contents($path, $svgContent);

        return $path;
    }

    /**
     * Export organogram to SVG format.
     *
     * @param Organization $organization
     * @param array $hierarchy
     * @param array $statistics
     * @return string SVG content
     */
    protected function exportToSvg(Organization $organization, array $hierarchy, array $statistics): string
    {
        return $this->generateSvgContent($hierarchy, $organization->name);
    }

    /**
     * Export organogram to JSON format.
     *
     * @param Organization $organization
     * @param array $hierarchy
     * @param array $statistics
     * @return array
     */
    protected function exportToJson(Organization $organization, array $hierarchy, array $statistics): array
    {
        return [
            'organization' => [
                'id' => $organization->id,
                'name' => $organization->name,
                'short_name' => $organization->short_name,
            ],
            'generated_at' => now()->toIso8601String(),
            'statistics' => $statistics,
            'hierarchy' => $hierarchy,
        ];
    }

    /**
     * Generate SVG content for the organogram.
     *
     * @param array $hierarchy
     * @param string $organizationName
     * @return string
     */
    protected function generateSvgContent(array $hierarchy, string $organizationName): string
    {
        // Calculate dimensions based on hierarchy depth and width
        $dimensions = $this->calculateSvgDimensions($hierarchy);
        $width = $dimensions['width'];
        $height = $dimensions['height'];

        $svg = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $svg .= '<svg xmlns="http://www.w3.org/2000/svg" width="' . $width . '" height="' . $height . '" viewBox="0 0 ' . $width . ' ' . $height . '">' . "\n";
        $svg .= '  <style>
            .node-rect { fill: #ffffff; stroke: #16a34a; stroke-width: 2; rx: 8; }
            .node-title { font-family: Arial, sans-serif; font-size: 12px; font-weight: bold; fill: #1f2937; }
            .node-assignee { font-family: Arial, sans-serif; font-size: 10px; fill: #6b7280; }
            .node-level { font-family: Arial, sans-serif; font-size: 8px; fill: #9ca3af; }
            .connector { stroke: #16a34a; stroke-width: 2; fill: none; }
            .header-text { font-family: Arial, sans-serif; font-size: 16px; font-weight: bold; fill: #16a34a; }
          </style>' . "\n";

        // Add header
        $svg .= '  <text x="' . ($width / 2) . '" y="30" text-anchor="middle" class="header-text">' . htmlspecialchars($organizationName) . ' - Organogram</text>' . "\n";

        // Add nodes
        $yOffset = 60;
        foreach ($hierarchy as $index => $node) {
            $xOffset = ($width / (count($hierarchy) + 1)) * ($index + 1);
            $svg .= $this->renderSvgNode($node, $xOffset, $yOffset, 0);
        }

        $svg .= '</svg>';

        return $svg;
    }

    /**
     * Render a single node as SVG.
     *
     * @param array $node
     * @param int $x
     * @param int $y
     * @param int $level
     * @return string
     */
    protected function renderSvgNode(array $node, int $x, int $y, int $level): string
    {
        $nodeWidth = 160;
        $nodeHeight = 60;
        $verticalGap = 80;

        $svg = '';

        // Node rectangle
        $rectX = $x - ($nodeWidth / 2);
        $rectY = $y;
        $svg .= '  <rect x="' . $rectX . '" y="' . $rectY . '" width="' . $nodeWidth . '" height="' . $nodeHeight . '" class="node-rect" />' . "\n";

        // Title
        $svg .= '  <text x="' . $x . '" y="' . ($y + 20) . '" text-anchor="middle" class="node-title">' . htmlspecialchars($node['title']) . '</text>' . "\n";

        // Assignee
        if ($node['assignee']) {
            $svg .= '  <text x="' . $x . '" y="' . ($y + 35) . '" text-anchor="middle" class="node-assignee">' . htmlspecialchars($node['assignee']) . '</text>' . "\n";
        }

        // Level
        $svg .= '  <text x="' . $x . '" y="' . ($y + 50) . '" text-anchor="middle" class="node-level">' . htmlspecialchars($node['hierarchy_level_label']) . '</text>' . "\n";

        // Children
        if (!empty($node['children'])) {
            $childY = $y + $nodeHeight + $verticalGap;
            $childCount = count($node['children']);
            $childSpacing = 180;
            $startX = $x - (($childCount - 1) * $childSpacing / 2);

            foreach ($node['children'] as $index => $child) {
                $childX = $startX + ($index * $childSpacing);

                // Connector line
                $svg .= '  <path d="M ' . $x . ' ' . ($y + $nodeHeight) . ' L ' . $x . ' ' . ($y + $nodeHeight + ($verticalGap / 2)) . ' L ' . $childX . ' ' . ($y + $nodeHeight + ($verticalGap / 2)) . ' L ' . $childX . ' ' . $childY . '" class="connector" />' . "\n";

                // Render child node
                $svg .= $this->renderSvgNode($child, $childX, $childY, $level + 1);
            }
        }

        return $svg;
    }

    /**
     * Calculate SVG dimensions based on hierarchy.
     *
     * @param array $hierarchy
     * @return array
     */
    protected function calculateSvgDimensions(array $hierarchy): array
    {
        $maxDepth = $this->calculateMaxDepth($hierarchy);
        $maxWidth = $this->calculateMaxWidth($hierarchy);

        $nodeWidth = 160;
        $nodeHeight = 60;
        $horizontalGap = 20;
        $verticalGap = 80;

        return [
            'width' => max(800, ($maxWidth * ($nodeWidth + $horizontalGap)) + 100),
            'height' => 100 + (($maxDepth + 1) * ($nodeHeight + $verticalGap)),
        ];
    }

    /**
     * Calculate maximum depth of the hierarchy.
     *
     * @param array $hierarchy
     * @return int
     */
    protected function calculateMaxDepth(array $hierarchy): int
    {
        $maxDepth = 0;
        foreach ($hierarchy as $node) {
            if (!empty($node['children'])) {
                $childDepth = $this->calculateMaxDepth($node['children']);
                $maxDepth = max($maxDepth, $childDepth + 1);
            }
        }
        return $maxDepth;
    }

    /**
     * Calculate maximum width at any level.
     *
     * @param array $hierarchy
     * @return int
     */
    protected function calculateMaxWidth(array $hierarchy): int
    {
        $widths = [count($hierarchy)];
        foreach ($hierarchy as $node) {
            if (!empty($node['children'])) {
                $widths[] = $this->calculateMaxWidth($node['children']);
            }
        }
        return max($widths);
    }
}
