<?php

namespace App\Services\Mentorship;

use App\Models\Mentorship\Category;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;

class CategoryService
{
    /**
     * Create a new category.
     */
    public function create(array $data): Category
    {
        DB::beginTransaction();
        
        try {
            // Generate slug if not provided
            if (empty($data['slug'])) {
                $data['slug'] = $this->generateUniqueSlug($data['name']);
            }

            $category = Category::create($data);

            DB::commit();

            Log::info('Mentorship category created', [
                'category_id' => $category->id,
                'name' => $category->name,
                'created_by' => auth()->id(),
            ]);

            return $category;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to create mentorship category', [
                'error' => $e->getMessage(),
                'data' => $data,
            ]);
            throw $e;
        }
    }

    /**
     * Update an existing category.
     */
    public function update(Category $category, array $data): Category
    {
        DB::beginTransaction();
        
        try {
            // Update slug if name changed and slug not provided
            if (isset($data['name']) && $data['name'] !== $category->name && empty($data['slug'])) {
                $data['slug'] = $this->generateUniqueSlug($data['name'], $category->id);
            }

            // Prevent setting self as parent
            if (isset($data['parent_id']) && $data['parent_id'] == $category->id) {
                throw new \Exception('A category cannot be its own parent.');
            }

            // Prevent circular reference
            if (isset($data['parent_id'])) {
                if ($this->wouldCreateCircularReference($category, $data['parent_id'])) {
                    throw new \Exception('This would create a circular reference in the category hierarchy.');
                }
            }

            $category->update($data);

            DB::commit();

            Log::info('Mentorship category updated', [
                'category_id' => $category->id,
                'name' => $category->name,
                'updated_by' => auth()->id(),
            ]);

            return $category->fresh();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to update mentorship category', [
                'error' => $e->getMessage(),
                'category_id' => $category->id,
            ]);
            throw $e;
        }
    }

    /**
     * Delete a category.
     */
    public function delete(Category $category): bool
    {
        DB::beginTransaction();
        
        try {
            // Check if category has children
            if ($category->hasChildren()) {
                throw new \Exception('Cannot delete category with sub-categories. Please move or delete them first.');
            }

            // Check if category has programmes
            if ($category->programmes()->exists()) {
                throw new \Exception('Cannot delete category with associated programmes.');
            }

            $category->delete();

            DB::commit();

            Log::info('Mentorship category deleted', [
                'category_id' => $category->id,
                'deleted_by' => auth()->id(),
            ]);

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to delete mentorship category', [
                'error' => $e->getMessage(),
                'category_id' => $category->id,
            ]);
            throw $e;
        }
    }

    /**
     * Activate a category.
     */
    public function activate(Category $category): bool
    {
        return $category->activate();
    }

    /**
     * Deactivate a category.
     */
    public function deactivate(Category $category): bool
    {
        // Check if deactivation would affect active programmes
        $activeProgrammesCount = $category->activeProgrammes()->count();
        if ($activeProgrammesCount > 0) {
            Log::warning('Deactivating category with active programmes', [
                'category_id' => $category->id,
                'active_programmes_count' => $activeProgrammesCount,
            ]);
        }

        return $category->deactivate();
    }

    /**
     * Get all categories as a tree structure.
     */
    public function getTree(bool $activeOnly = false): array
    {
        $query = Category::with('children.children');

        if ($activeOnly) {
            $query->active();
        }

        $categories = $query->parents()->ordered()->get();

        return $this->buildTree($categories);
    }

    /**
     * Get all categories as a flat list with depth indication.
     */
    public function getFlatList(bool $activeOnly = false)
    {
        $query = Category::with('parent');

        if ($activeOnly) {
            $query->active();
        }

        return $query->ordered()->get()->map(function ($category) {
            $category->depth = $category->depth;
            $category->indent = str_repeat('— ', $category->depth);
            return $category;
        });
    }

    /**
     * Get categories with programme counts.
     */
    public function getWithProgrammeCounts(bool $activeOnly = true)
    {
        $query = Category::withCount(['programmes', 'activeProgrammes']);

        if ($activeOnly) {
            $query->active();
        }

        return $query->ordered()->get();
    }

    /**
     * Get active categories for dropdown/select.
     */
    public function getForSelect(bool $activeOnly = true): array
    {
        $query = Category::query();

        if ($activeOnly) {
            $query->active();
        }

        return $query->ordered()->pluck('name', 'id')->toArray();
    }

    /**
     * Move category to new parent.
     */
    public function moveTo(Category $category, ?int $newParentId): Category
    {
        if ($newParentId == $category->id) {
            throw new \Exception('A category cannot be its own parent.');
        }

        if ($newParentId && $this->wouldCreateCircularReference($category, $newParentId)) {
            throw new \Exception('This would create a circular reference in the category hierarchy.');
        }

        $category->update(['parent_id' => $newParentId]);

        Log::info('Category moved', [
            'category_id' => $category->id,
            'new_parent_id' => $newParentId,
            'moved_by' => auth()->id(),
        ]);

        return $category->fresh();
    }

    /**
     * Update sort order.
     */
    public function updateSortOrder(array $order): bool
    {
        DB::beginTransaction();
        
        try {
            foreach ($order as $index => $categoryId) {
                Category::where('id', $categoryId)->update(['sort_order' => $index]);
            }

            DB::commit();

            Log::info('Category sort order updated', [
                'order' => $order,
                'updated_by' => auth()->id(),
            ]);

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to update category sort order', [
                'error' => $e->getMessage(),
            ]);
            throw $e;
        }
    }

    /**
     * Get category statistics.
     */
    public function getStatistics(Category $category): array
    {
        return [
            'programmes'         => $category->programmes()->count(),
            'active_programmes'  => $category->activeProgrammes()->count(),
            'children'           => $category->children()->count(),
            'applications'       => $category->applications()->count(),
            // Legacy aliases kept for backward compatibility
            'programmes_count'        => $category->programmes()->count(),
            'active_programmes_count' => $category->activeProgrammes()->count(),
            'children_count'          => $category->children()->count(),
            'applications_count'      => $category->applications()->count(),
        ];
    }

    /**
     * Generate a unique slug.
     */
    protected function generateUniqueSlug(string $name, ?int $excludeId = null): string
    {
        $slug = Str::slug($name);
        $originalSlug = $slug;
        $count = 1;

        while (Category::where('slug', $slug)
            ->when($excludeId, fn($q) => $q->where('id', '!=', $excludeId))
            ->exists()) {
            $slug = $originalSlug . '-' . $count++;
        }

        return $slug;
    }

    /**
     * Check if setting a parent would create a circular reference.
     */
    protected function wouldCreateCircularReference(Category $category, int $newParentId): bool
    {
        $parent = Category::find($newParentId);

        while ($parent) {
            if ($parent->id === $category->id) {
                return true;
            }
            $parent = $parent->parent;
        }

        return false;
    }

    /**
     * Build tree structure from categories.
     */
    protected function buildTree($categories): array
    {
        return $categories->map(function ($category) {
            return [
                'id' => $category->id,
                'name' => $category->name,
                'slug' => $category->slug,
                'description' => $category->description,
                'is_active' => $category->is_active,
                'icon' => $category->icon,
                'color' => $category->color,
                'programmes_count' => $category->programmes_count ?? $category->programmes()->count(),
                'children' => $this->buildTree($category->children),
            ];
        })->toArray();
    }
}
