<?php

namespace App\Services;

use App\Models\Component;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;

class ComponentScannerService
{
    private readonly string $includesPath;
    private array $config;

    public function __construct()
    {
        $this->includesPath = base_path('sites/default/templates/includes');
        $this->config = config('cache.component_registry');
    }

    /**
     * Discover all Twig components with caching
     */
    public function discoverComponents(): array
    {
        $cacheKey = $this->config['prefix'].'_components';

        if ($this->config['enabled'] && Cache::has($cacheKey)) {
            $cached = Cache::get($cacheKey);
            Log::debug("Component discovery: Using cached results ({$cached['stats']['total']} components)");

            return $cached;
        }

        $startTime = microtime(true);
        $components = $this->scanDirectory();
        $categories = $this->generateCategories($components);
        $scanTime = microtime(true) - $startTime;

        $result = [
            'components' => $components,
            'categories' => $categories,
            'stats' => [
                'total' => count($components),
                'categories' => count($categories),
                'scan_time' => round($scanTime * 1000, 2).'ms',
                'generated_at' => now()->toISOString(),
            ],
        ];

        if ($this->config['enabled']) {
            Cache::put($cacheKey, $result, $this->config['ttl']);
            Log::debug(
                "Component discovery: Cached {$result['stats']['total']} components in {$result['stats']['scan_time']}",
            );
        }

        return $result;
    }

    /**
     * Find a specific component by path
     */
    public function findComponent(string $relativePath): ?Component
    {
        $components = $this->discoverComponents()['components'];

        foreach ($components as $component) {
            if ($component->path === $relativePath) {
                return $component;
            }
        }

        return null;
    }

    /**
     * Scan directory for Twig components
     */
    private function scanDirectory(): array
    {
        $components = [];

        if (! File::exists($this->includesPath)) {
            Log::warning("Component includes directory not found: {$this->includesPath}");

            return $components;
        }

        try {
            $files = File::allFiles($this->includesPath);

            foreach ($files as $file) {
                if ($file->getExtension() !== 'twig') { continue; }

$relativePath = $this->getRelativePath($file->getRealPath());
                    $component = Component::fromFilePath($file->getRealPath(), $relativePath);

                    // Test renderability
                    $this->testComponentRenderability($component);

                    $components[] = $component;

                    // Limit to 200 components as per specification
                    if (count($components) >= 200) {
                        break;
                    }
            }
        } catch (\Exception $e) {
            Log::error('Error scanning components directory: '.$e->getMessage());
        }

        // Sort by category then by name
        usort($components, static function ($a, $b) {
            $categoryCompare = strcmp($a->category, $b->category);
            if ($categoryCompare !== 0) {
                return $categoryCompare;
            }

            return strcmp($a->name, $b->name);
        });

        return $components;
    }

    /**
     * Get relative path from includes directory
     */
    private function getRelativePath(string $absolutePath): string
    {
        $includesPath = realpath($this->includesPath);
        $filePath = realpath($absolutePath);

        if (str_starts_with($filePath, $includesPath)) {
            return substr($filePath, strlen($includesPath) + 1);
        }

        return basename($absolutePath);
    }

    /**
     * Test if component can be rendered
     */
    private function testComponentRenderability(Component $component): void
    {
        try {
            // Basic file validation
            if (! file_exists($component->absolute_path)) {
                $component->markAsError('File not found');

                return;
            }

            // Check file is readable
            if (! is_readable($component->absolute_path)) {
                $component->markAsError('File not readable');

                return;
            }

            // Basic Twig syntax check
            $content = file_get_contents($component->absolute_path);
            if ($content === false) {
                $component->markAsError('Cannot read file content');

                return;
            }

            // Check for obvious Twig syntax errors
            if ($this->hasObviousTwigErrors($content)) {
                $component->markAsError('Twig syntax errors detected');

                return;
            }

            // Component is renderable
            $component->is_renderable = true;
            $component->error_message = null;
        } catch (\Exception $e) {
            $component->markAsError('Error testing component: '.$e->getMessage());
        }
    }

    /**
     * Check for obvious Twig syntax errors
     */
    private function hasObviousTwigErrors(string $content): bool
    {
        // More sophisticated Twig syntax checking
        // Use regex to match actual Twig syntax, handling multiline content

        // Count Twig expressions {{ ... }} with DOTALL flag for multiline support
        preg_match_all('/\{\{.*?\}\}/s', $content, $expressions);
        $expressionCount = count($expressions[0]);

        // Count opening {{ that should have closing }}
        preg_match_all('/\{\{/', $content, $openExpressions);
        $openExpressionCount = count($openExpressions[0]);

        // Count Twig statements {% ... %} with DOTALL flag for multiline support
        preg_match_all('/\{%.*?%\}/s', $content, $statements);
        $statementCount = count($statements[0]);

        // Count opening {% that should have closing %}
        preg_match_all('/\{%/', $content, $openStatements);
        $openStatementCount = count($openStatements[0]);

        // Check for unmatched Twig syntax
        if ($openExpressionCount !== $expressionCount) {
            return true; // Unmatched {{ }}
        }

        if ($openStatementCount !== $statementCount) {
            return true; // Unmatched {% %}
        }

        // Additional check for block statement pairs (if/endif, for/endfor, etc.)
        $blockPatterns = [
            'if' => 'endif',
            'for' => 'endfor',
            'block' => 'endblock',
            'macro' => 'endmacro',
            'set' => false, // set doesn't require an end tag
        ];

        foreach ($blockPatterns as $start => $end) {
            if ($end === false)
                continue; // Skip tags that don't require end tags

            $startCount = preg_match_all('/\{%\s*'.preg_quote($start).'\b/', $content);
            $endCount = preg_match_all('/\{%\s*'.preg_quote($end).'\b/', $content);

            if ($startCount !== $endCount) {
                return true; // Unmatched block statements
            }
        }

        return false; // No obvious syntax errors found
    }

    /**
     * Generate categories from components
     */
    private function generateCategories(array $components): array
    {
        $categoryMap = [];

        foreach ($components as $component) {
            $category = $component->category;

            if (! isset($categoryMap[$category])) {
                $categoryMap[$category] = [
                    'name' => $this->generateCategoryName($category),
                    'slug' => $this->generateCategorySlug($category),
                    'component_count' => 0,
                ];
            }

            $categoryMap[$category]['component_count']++;
        }

        // Convert to indexed array and sort by name
        $categories = array_values($categoryMap);
        usort($categories, static fn($a, $b) => strcmp((string) $a['name'], (string) $b['name']));

        return $categories;
    }

    /**
     * Generate category display name
     */
    private function generateCategoryName(string $category): string
    {
        $parts = explode('/', $category);
        $parts = array_map(static fn($part) => ucwords(str_replace(['_', '-'], ' ', $part)), $parts);

        return implode(' / ', $parts);
    }

    /**
     * Generate category slug
     */
    private function generateCategorySlug(string $category): string
    {
        return str_replace('/', '-', $category);
    }

    /**
     * Clear component cache
     */
    public function clearCache(): void
    {
        $cacheKey = $this->config['prefix'].'_components';
        Cache::forget($cacheKey);
    }

    /**
     * Get components filtered by category
     */
    public function getComponentsByCategory(string $category): array
    {
        $discovery = $this->discoverComponents();

        return array_filter($discovery['components'], static fn($component) => $component->category === $category);
    }

    /**
     * Search components by name or description
     */
    public function searchComponents(string $query): array
    {
        $discovery = $this->discoverComponents();
        $queryLower = strtolower($query);

        return array_filter($discovery['components'], static fn($component) => str_contains(strtolower((string) $component->name), $queryLower)
        || str_contains(strtolower((string) $component->description), $queryLower));
    }
}
