<?php

namespace App\Services\Inventory;

use App\Models\ClassSection;
use App\Models\InventoryItem;
use App\Models\InventoryMovement;
use App\Models\StudentClass;
use App\Models\TeacherClass;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class InventoryTeacherDistributionService
{
    public function __construct(
        private InventoryContextService $context,
        private InventoryMovementService $movementService
    ) {
    }

    public function formData(int $userId, ?int $classSectionId, ?int $itemId): array
    {
        $ctx = $this->buildTeacherClassContext($userId);
        if (!$ctx['ok']) {
            return $ctx;
        }

        $classIds = $ctx['classSectionIds'];
        $lockClassSection = count($classIds) === 1;
        $selectedClassId = $lockClassSection
            ? (int) ($ctx['defaultClassSectionId'] ?? 0)
            : (int) ($classSectionId ?? ($ctx['defaultClassSectionId'] ?? 0));

        $classID = null;
        if (!empty($selectedClassId)) {
            $classID = ClassSection::getClassId($selectedClassId);
            $classID = $classID !== null ? (int) $classID : null;
        }

        $builder = DB::table('inventory_items as i')
            ->select('i.id', 'i.name', 'i.isbn', 'i.edition', 'i.quantity', 'i.category_id', 'i.type', 'c.name as category_name', 'c.grade_min', 'c.grade_max')
            ->leftJoin('inventory_categories as c', 'c.id', '=', 'i.category_id')
            ->where('i.type', 'book');

        if (!empty($classID)) {
            $builder->where(function ($q) use ($classID) {
                $q->where(function ($inner) use ($classID) {
                    $inner->whereNotNull('c.grade_min')
                        ->whereNotNull('c.grade_max')
                        ->where('c.grade_min', '<=', $classID)
                        ->where('c.grade_max', '>=', $classID);
                })->orWhere(function ($inner) use ($classID) {
                    $inner->whereNotNull('c.grade_min')
                        ->whereNull('c.grade_max')
                        ->where('c.grade_min', $classID);
                })->orWhere(function ($inner) use ($classID) {
                    $inner->whereNotNull('c.grade_max')
                        ->whereNull('c.grade_min')
                        ->where('c.grade_max', $classID);
                });
            });
        }

        $rows = $builder->orderBy('i.name')->get()->map(fn ($r) => (array) $r)->all();

        $labelFor = static function (?array $r): string {
            $name = $r['category_name'] ?? 'Uncategorized';
            $gmin = $r['grade_min'] ?? null;
            $gmax = $r['grade_max'] ?? null;
            if ($gmin === null && $gmax === null) return $name;
            if ($gmin !== null && $gmax !== null) return $name . ' (G' . (int) $gmin . '–G' . (int) $gmax . ')';
            if ($gmin !== null) return $name . ' (G' . (int) $gmin . '+)';
            return $name . ' (≤G' . (int) $gmax . ')';
        };

        $booksGrouped = [];
        foreach ($rows as $r) {
            $catId = (int) ($r['category_id'] ?? 0);
            if (!isset($booksGrouped[$catId])) {
                $booksGrouped[$catId] = [
                    'label' => $labelFor($r),
                    'items' => [],
                ];
            }
            $display = $r['name'];
            if (!empty($r['isbn'])) {
                $display .= ' — ISBN ' . $r['isbn'];
            }
            if (!empty($r['edition'])) {
                $display .= ' (' . $r['edition'] . ')';
            }

            $booksGrouped[$catId]['items'][] = [
                'id' => (int) $r['id'],
                'display' => $display,
                'quantity' => (int) ($r['quantity'] ?? 0),
            ];
        }

        uasort($booksGrouped, fn ($a, $b) => strnatcasecmp($a['label'], $b['label']));
        foreach ($booksGrouped as &$grp) {
            usort($grp['items'], fn ($x, $y) => strnatcasecmp($x['display'], $y['display']));
        }
        unset($grp);

        $students = $ctx['studentsData'][$selectedClassId] ?? [];

        $already = [];
        $itemId = (int) ($itemId ?? 0);
        if ($itemId && $selectedClassId) {
            $rowsHist = InventoryMovement::query()
                ->selectRaw('student_id, SUM(CASE WHEN qty_change < 0 THEN -qty_change ELSE 0 END) AS qty')
                ->where([
                    'item_id' => $itemId,
                    'movement_type' => 'distribution',
                    'class_section_id' => $selectedClassId,
                    'school_year' => $ctx['schoolYear'],
                ])
                ->groupBy('student_id')
                ->get()
                ->map(fn ($r) => (array) $r)
                ->all();
            foreach ($rowsHist as $r) {
                $sid = (int) ($r['student_id'] ?? 0);
                if ($sid) $already[$sid] = (int) ($r['qty'] ?? 0);
            }
        }

        $onHand = 0;
        if ($itemId) {
            $book = InventoryItem::query()->find($itemId);
            if ($book && $book->type === 'book') {
                $onHand = (int) ($book->quantity ?? 0);
            }
        }

        return [
            'ok' => true,
            'booksGrouped' => $booksGrouped,
            'items' => $rows,
            'classes' => $ctx['classes'],
            'class_section_id' => $selectedClassId,
            'lockClassSection' => $lockClassSection,
            'item_id' => $itemId,
            'students' => $students,
            'already' => $already,
            'onHand' => $onHand,
            'schoolYear' => $ctx['schoolYear'],
            'semester' => $ctx['semester'],
        ];
    }

    public function distribute(int $userId, array $payload): array
    {
        $ctx = $this->buildTeacherClassContext($userId);
        if (!$ctx['ok']) {
            return $ctx;
        }

        $itemId = (int) ($payload['item_id'] ?? 0);
        $postedClassId = (int) ($payload['class_section_id'] ?? 0);
        $selectedIds = array_values(array_unique(array_map('intval', $payload['student_ids'] ?? [])));
        $note = (string) ($payload['note'] ?? '');

        $classIds = $ctx['classSectionIds'];
        $classSectionId = (count($classIds) === 1)
            ? (int) $classIds[0]
            : (in_array($postedClassId, $classIds, true) ? $postedClassId : 0);

        if (!$classSectionId) {
            return ['ok' => false, 'message' => 'Invalid class selection.'];
        }

        $book = InventoryItem::query()->find($itemId);
        if (!$book || $book->type !== 'book') {
            return ['ok' => false, 'message' => 'Invalid book.'];
        }

        $students = $ctx['studentsData'][$classSectionId] ?? [];
        $validIds = [];
        foreach ($students as $st) {
            $sid = $st['student_id'] ?? ($st['id'] ?? null);
            if ($sid) $validIds[(int) $sid] = true;
        }
        $selectedIds = array_values(array_filter($selectedIds, fn ($sid) => isset($validIds[$sid])));

        $already = [];
        $rows = InventoryMovement::query()
            ->selectRaw('student_id, SUM(CASE WHEN qty_change < 0 THEN -qty_change ELSE 0 END) AS qty')
            ->where([
                'item_id' => $itemId,
                'movement_type' => 'distribution',
                'class_section_id' => $classSectionId,
                'school_year' => $ctx['schoolYear'],
            ])
            ->groupBy('student_id')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
        foreach ($rows as $r) {
            $sid = (int) ($r['student_id'] ?? 0);
            if ($sid) $already[$sid] = (int) ($r['qty'] ?? 0);
        }

        $toGive = [];
        foreach ($selectedIds as $sid) {
            if (($already[$sid] ?? 0) < 1) {
                $toGive[] = $sid;
            }
        }

        $needed = count($toGive);
        $onHand = (int) ($book->quantity ?? 0);
        if ($needed > $onHand) {
            return ['ok' => false, 'message' => 'Not enough stock to complete distribution.'];
        }

        $okCount = 0;
        foreach ($toGive as $sid) {
            $ok = $this->movementService->recordMovement($itemId, -1, 'distribution', 'Teacher distribution', $note, $classSectionId, $sid, $userId);
            if ($ok) {
                $okCount++;
            }
        }

        return [
            'ok' => true,
            'distributed' => $okCount,
            'already_assigned' => count($selectedIds) - $okCount,
        ];
    }

    private function buildTeacherClassContext(int $userId): array
    {
        if ($userId <= 0) {
            return ['ok' => false, 'message' => 'Please log in first.'];
        }

        $classes = TeacherClass::getClassAssignmentsByUserId($userId, $this->context->schoolYear());
        if (empty($classes)) {
            return ['ok' => false, 'message' => 'You do not have an assigned class yet.'];
        }

        $user = User::query()->find($userId);
        if (!$user) {
            return ['ok' => false, 'message' => 'User not found.'];
        }

        $roles = DB::table('user_roles as ur')
            ->join('roles as r', 'r.id', '=', 'ur.role_id')
            ->where('ur.user_id', $userId)
            ->pluck('r.name')
            ->toArray();

        if (!in_array('teacher', $roles, true) && !in_array('teacher_assistant', $roles, true)) {
            return ['ok' => false, 'message' => 'Access denied.'];
        }

        $classSectionIds = array_column($classes, 'class_section_id');
        $studentsData = [];
        if (!empty($classSectionIds)) {
            $students = StudentClass::getStudentsByClassSectionIds($classSectionIds);
            foreach ($students as $student) {
                $studentsData[$student['class_section_id']][] = $student;
            }
        }

        $taAssignments = TeacherClass::query()
            ->whereIn('class_section_id', $classSectionIds)
            ->where('school_year', $this->context->schoolYear())
            ->where('position', 'ta')
            ->get()
            ->toArray();

        $taNames = [];
        foreach ($taAssignments as $ta) {
            $taUser = User::query()->find($ta['teacher_id'] ?? 0);
            if ($taUser) {
                $csid = $ta['class_section_id'];
                $taNames[$csid][] = trim(($taUser->firstname ?? '') . ' ' . ($taUser->lastname ?? ''));
            }
        }

        $defaultClassSectionId = $classSectionIds[0] ?? null;

        return [
            'ok' => true,
            'classes' => $classes,
            'classSectionIds' => $classSectionIds,
            'studentsData' => $studentsData,
            'taNames' => $taNames,
            'defaultClassSectionId' => $defaultClassSectionId,
            'schoolYear' => $this->context->schoolYear(),
            'semester' => $this->context->semester(),
        ];
    }
}
