<?php

namespace App\Services\Mentorship;

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

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

            // Set created_by
            if (auth()->check()) {
                $data['created_by'] = auth()->id();
            }

            $programme = Programme::create($data);

            DB::commit();

            Log::info('Mentorship programme created', [
                'programme_id' => $programme->id,
                'title' => $programme->title,
                'created_by' => auth()->id(),
            ]);

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

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

            // Set updated_by
            if (auth()->check()) {
                $data['updated_by'] = auth()->id();
            }

            $programme->update($data);

            DB::commit();

            Log::info('Mentorship programme updated', [
                'programme_id' => $programme->id,
                'title' => $programme->title,
                'updated_by' => auth()->id(),
            ]);

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

    /**
     * Delete a programme (soft delete).
     */
    public function delete(Programme $programme): bool
    {
        DB::beginTransaction();
        
        try {
            // Check if programme has active assignments
            if ($programme->assignments()->active()->exists()) {
                throw new \Exception('Cannot delete programme with active assignments.');
            }

            $programme->delete();

            DB::commit();

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

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

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

    /**
     * Deactivate a programme.
     */
    public function deactivate(Programme $programme): bool
    {
        return $programme->deactivate();
    }

    /**
     * Feature a programme.
     */
    public function feature(Programme $programme): bool
    {
        return $programme->update(['is_featured' => true]);
    }

    /**
     * Unfeature a programme.
     */
    public function unfeature(Programme $programme): bool
    {
        return $programme->update(['is_featured' => false]);
    }

    /**
     * Get programmes with filtering and pagination.
     */
    public function getPaginated(array $filters = [], int $perPage = 15)
    {
        $query = Programme::with(['category', 'creator']);

        // Filter by active status
        if (isset($filters['is_active'])) {
            $query->where('is_active', $filters['is_active']);
        }

        // Filter by featured
        if (isset($filters['is_featured'])) {
            $query->where('is_featured', $filters['is_featured']);
        }

        // Filter by category
        if (!empty($filters['category_id'])) {
            $query->where('category_id', $filters['category_id']);
        }

        // Filter by programme type
        if (!empty($filters['programme_type'])) {
            $query->where('programme_type', $filters['programme_type']);
        }

        // Filter by session type
        if (!empty($filters['session_type'])) {
            $query->where('session_type', $filters['session_type']);
        }

        // Filter by free/paid
        if (isset($filters['is_free'])) {
            $query->where('is_free', $filters['is_free']);
        }

        // Filter open for registration
        if (!empty($filters['open_for_registration'])) {
            $query->openForRegistration();
        }

        // Search by title
        if (!empty($filters['search'])) {
            $query->where(function ($q) use ($filters) {
                $q->where('title', 'like', '%' . $filters['search'] . '%')
                    ->orWhere('description', 'like', '%' . $filters['search'] . '%');
            });
        }

        // Sort
        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 'desc';
        $query->orderBy($sortBy, $sortOrder);

        return $query->paginate($perPage);
    }

    /**
     * Get active programmes for public display.
     */
    public function getActive(array $filters = [])
    {
        $query = Programme::with(['category'])
            ->active()
            ->openForRegistration();

        // Filter by category
        if (!empty($filters['category_id'])) {
            $query->where('category_id', $filters['category_id']);
        }

        // Featured first
        $query->orderBy('is_featured', 'desc')
            ->orderBy('created_at', 'desc');

        return $query->get();
    }

    /**
     * Get featured programmes.
     */
    public function getFeatured(int $limit = 6)
    {
        return Programme::with(['category'])
            ->active()
            ->featured()
            ->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();
    }

    /**
     * Get programme statistics.
     */
    public function getStatistics(Programme $programme): array
    {
        return [
            'applications_count' => $programme->applications()->count(),
            'pending_applications' => $programme->applications()->pending()->count(),
            'approved_applications' => $programme->applications()->approved()->count(),
            'active_assignments' => $programme->assignments()->active()->count(),
            'completed_assignments' => $programme->assignments()->completed()->count(),
            'active_mentors' => $programme->getActiveMentorsCount(),
            'active_mentees' => $programme->getActiveMenteesCount(),
            'sessions_count' => $programme->sessions()->count(),
            'completed_sessions' => $programme->sessions()->completed()->count(),
            'certificates_issued' => $programme->certificates()->count(),
        ];
    }

    /**
     * Update programme statistics.
     */
    public function updateStatistics(Programme $programme): void
    {
        $programme->updateStatistics();
    }

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

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

        return $slug;
    }

    /**
     * Check if a programme can accept new applications.
     */
    public function canAcceptApplications(Programme $programme): bool
    {
        if (!$programme->is_active) {
            return false;
        }

        // Check registration period
        if ($programme->registration_start && $programme->registration_start->isFuture()) {
            return false;
        }

        if ($programme->registration_end && $programme->registration_end->isPast()) {
            return false;
        }

        // Check capacity
        if (!$programme->hasMentorSlots() && !$programme->hasMenteeSlots()) {
            return false;
        }

        return true;
    }

    /**
     * Get programmes by category.
     */
    public function getByCategory(Category $category, int $limit = 12)
    {
        return Programme::with(['category'])
            ->active()
            ->where('category_id', $category->id)
            ->orderBy('is_featured', 'desc')
            ->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();
    }

    /**
     * Search programmes.
     */
    public function search(string $query, int $limit = 10)
    {
        return Programme::with(['category'])
            ->active()
            ->where(function ($q) use ($query) {
                $q->where('title', 'like', '%' . $query . '%')
                    ->orWhere('description', 'like', '%' . $query . '%')
                    ->orWhere('objectives', 'like', '%' . $query . '%');
            })
            ->orderBy('is_featured', 'desc')
            ->limit($limit)
            ->get();
    }
}
