<?php

namespace App\Services;

use App\Models\Chapter;
use App\Models\Homework;
use App\Models\HomeworkQuestion;
use App\Models\Subject;
use App\Models\Topic;
use App\Models\User;
use App\Support\PaperTypeHelper;
use App\Support\TeachingHomeworkLayout;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;

/**
 * Student Self Homework — same chapter + topic generate as Self Exam.
 */
class SelfHomeworkGeneratorService
{
    public const MARK_OPTIONS = [20, 30, 50, 70, 100];

    public function __construct(
        private readonly QuestionPaperGeneratorService $generator,
    ) {}

    public static function hasSelfHomeworkColumns(): bool
    {
        static $cached = null;
        if ($cached !== null) {
            return $cached;
        }
        try {
            $cached = Schema::hasColumn('homeworks', 'is_self_homework')
                && Schema::hasColumn('homeworks', 'student_id');
        } catch (\Throwable) {
            $cached = false;
        }

        return $cached;
    }

    /**
     * @return array{
     *     target_marks: int,
     *     type_counts: array<string, int>,
     *     marks_per_type: array<string, int>,
     *     available: array<string, int>,
     *     breakdown: array,
     *     total_marks: int,
     *     total_questions: int,
     *     layout_total_marks: int
     * }
     */
    public function buildPlan(int $chapterId, ?int $topicId, int $targetMarks): array
    {
        $this->guardMarks($targetMarks);

        $layout = TeachingHomeworkLayout::autoLayout($targetMarks);
        $available = $this->generator->availableCounts($chapterId, $topicId);
        $typeCounts = $this->fitCountsToAvailable($layout['type_counts'], $available);

        if ($typeCounts === []) {
            throw ValidationException::withMessages([
                'chapter_id' => 'Not enough questions in this chapter/topic. Choose another or lower marks.',
            ]);
        }

        $marksPerType = PaperTypeHelper::normalizeMarksPerType($layout['marks_per_type'], $typeCounts);

        return [
            'target_marks' => $targetMarks,
            'type_counts' => $typeCounts,
            'marks_per_type' => $marksPerType,
            'available' => $available,
            'breakdown' => PaperTypeHelper::breakdown($typeCounts, $marksPerType),
            'total_marks' => PaperTypeHelper::totalMarks($typeCounts, $marksPerType),
            'total_questions' => PaperTypeHelper::totalQuestions($typeCounts),
            'layout_total_marks' => $layout['total_marks'],
        ];
    }

    /**
     * @param  array<string, int>  $requestedCounts
     * @return array{
     *     target_marks: int,
     *     type_counts: array<string, int>,
     *     marks_per_type: array<string, int>,
     *     available: array<string, int>,
     *     breakdown: array,
     *     total_marks: int,
     *     total_questions: int,
     *     layout_total_marks: int
     * }
     */
    public function buildCustomPlan(int $chapterId, ?int $topicId, int $targetMarks, array $requestedCounts): array
    {
        $this->guardMarks($targetMarks);

        $available = $this->generator->availableCounts($chapterId, $topicId);
        $typeCounts = PaperTypeHelper::normalizeCounts($requestedCounts);

        if ($typeCounts === []) {
            throw ValidationException::withMessages([
                'type_counts' => 'Enter at least one question in "In this paper".',
            ]);
        }

        foreach ($typeCounts as $type => $count) {
            if ($count > (int) ($available[$type] ?? 0)) {
                throw ValidationException::withMessages([
                    "type_counts.{$type}" => PaperTypeHelper::label($type).' has only '.($available[$type] ?? 0).' question(s) available.',
                ]);
            }
        }

        $marksPerType = PaperTypeHelper::normalizeMarksPerType(TeachingHomeworkLayout::marksPerType(), $typeCounts);

        return [
            'target_marks' => $targetMarks,
            'type_counts' => $typeCounts,
            'marks_per_type' => $marksPerType,
            'available' => $available,
            'breakdown' => PaperTypeHelper::breakdown($typeCounts, $marksPerType),
            'total_marks' => PaperTypeHelper::totalMarks($typeCounts, $marksPerType),
            'total_questions' => PaperTypeHelper::totalQuestions($typeCounts),
            'layout_total_marks' => $targetMarks,
        ];
    }

    public function generate(
        User $student,
        Subject $subject,
        int $chapterId,
        ?int $topicId,
        int $targetMarks,
        array $requestedCounts = [],
    ): Homework {
        if (! self::hasSelfHomeworkColumns()) {
            throw ValidationException::withMessages([
                'subject_id' => 'Self Homework DB columns are missing. Run MySQL ALTER for student_id / is_self_homework.',
            ]);
        }

        $chapter = Chapter::query()
            ->whereKey($chapterId)
            ->where('subject_id', $subject->id)
            ->where('is_active', true)
            ->first();

        if (! $chapter) {
            throw ValidationException::withMessages([
                'chapter_id' => 'Invalid chapter for this subject.',
            ]);
        }

        $topic = null;
        if ($topicId) {
            $topic = Topic::query()
                ->whereKey($topicId)
                ->where('chapter_id', $chapter->id)
                ->where('is_active', true)
                ->first();

            if (! $topic) {
                throw ValidationException::withMessages([
                    'topic_id' => 'Invalid topic for this chapter.',
                ]);
            }
        }

        $plan = $requestedCounts !== []
            ? $this->buildCustomPlan($chapter->id, $topic?->id, $targetMarks, $requestedCounts)
            : $this->buildPlan($chapter->id, $topic?->id, $targetMarks);
        $generated = $this->generator->generate(
            $chapter->id,
            $topic?->id,
            $plan['type_counts'],
            $plan['marks_per_type']
        );

        $topicLabel = $topic?->name ?? 'All topics';
        $finalMarks = (int) $generated['total_marks'];

        $homework = Homework::create([
            'teacher_id' => $student->id,
            'student_id' => $student->id,
            'is_self_homework' => true,
            'standard' => $student->standard,
            'subject_id' => $subject->id,
            'chapter_id' => $chapter->id,
            'topic_id' => $topic?->id,
            'title' => "Self Homework ({$finalMarks} marks) — {$subject->name}",
            'description' => "{$chapter->name} · {$topicLabel}",
            'due_at' => now()->addDays(30)->endOfDay(),
            'status' => 'published',
            'generation_config' => [
                'type_counts' => $plan['type_counts'],
                'marks_per_type' => $generated['marks_per_type'],
                'question_ids' => $generated['questions']->pluck('id')->all(),
                'target_marks' => $targetMarks,
                'selected_marks' => $finalMarks,
                'total_marks' => $generated['total_marks'],
                'chapter_id' => $chapter->id,
                'topic_id' => $topic?->id,
                'generated_from' => 'self_homework',
            ],
        ]);

        $this->syncQuestions($homework, $generated['questions']);

        return $homework;
    }

    private function guardMarks(int $targetMarks): void
    {
        if (! in_array($targetMarks, self::MARK_OPTIONS, true)) {
            throw ValidationException::withMessages([
                'target_marks' => 'Please choose 20, 30, 50, 70, or 100 marks.',
            ]);
        }
    }

    /**
     * @param  array<string, int>  $desired
     * @param  array<string, int>  $available
     * @return array<string, int>
     */
    private function fitCountsToAvailable(array $desired, array $available): array
    {
        $fitted = [];
        foreach ($desired as $type => $count) {
            $use = min(max(0, (int) $count), (int) ($available[$type] ?? 0));
            if ($use > 0) {
                $fitted[$type] = $use;
            }
        }

        return PaperTypeHelper::normalizeCounts($fitted);
    }

    private function syncQuestions(Homework $homework, Collection $questions): void
    {
        $homework->questions()->delete();
        $sort = 0;
        foreach ($questions as $question) {
            HomeworkQuestion::create([
                'homework_id' => $homework->id,
                'chapter_question_id' => $question->id,
                'question_type' => $question->question_type,
                'question_text' => $question->question_text,
                'options' => $question->options,
                'answer' => $question->answer,
                'sort_order' => $sort++,
            ]);
        }
    }
}
