<?php

namespace App\Http\Controllers\Api\Badges;

use App\Http\Controllers\Controller;
use App\Services\Badges\BadgeFormDataService;
use App\Services\Badges\BadgePdfService;
use App\Services\Badges\BadgePrintLogService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Throwable;

class BadgeController extends Controller
{
    public function __construct(
        protected BadgePdfService $badgePdfService,
        protected BadgePrintLogService $badgePrintLogService,
        protected BadgeFormDataService $badgeFormDataService
    ) {
    }

    public function generatePdf(Request $request)
    {
        $guard = $this->authenticatedUserIdOrUnauthorized();
        if ($guard instanceof JsonResponse) {
            return $guard;
        }

        $validator = Validator::make($request->all(), [
            'user_ids' => ['nullable', 'array'],
            'user_ids.*' => ['integer', 'exists:users,id'],
            'student_ids' => ['nullable', 'array'],
            'student_ids.*' => ['integer', 'exists:students,id'],
            'school_year' => ['nullable', 'string', 'max:50'],
            'roles' => ['nullable', 'array'],
            'classes' => ['nullable', 'array'],
        ]);

        $validator->after(function ($validator): void {
            $data = $validator->getData();
            $users = array_values(array_filter((array) ($data['user_ids'] ?? []), static fn ($v) => (int) $v > 0));
            $students = array_values(array_filter((array) ($data['student_ids'] ?? []), static fn ($v) => (int) $v > 0));
            if ($users === [] && $students === []) {
                $validator->errors()->add('user_ids', 'Provide at least one user id or student id.');
            }
        });

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $data = $validator->validated();

        return $this->badgePdfService->generate(
            studentIds: $data['student_ids'] ?? [],
            userIds: $data['user_ids'] ?? [],
            schoolYear: $data['school_year'] ?? null,
            rolesMap: $data['roles'] ?? [],
            classesMap: $data['classes'] ?? [],
            actorId: $guard
        );
    }

    public function printStatus(Request $request): JsonResponse
    {
        try {
            return response()->json([
                'ok' => true,
                'data' => $this->badgePrintLogService->getStatus(
                    $this->parseCombinedBadgeIds($request),
                    $request->input('school_year')
                ),
            ]);
        } catch (Throwable) {
            return response()->json([
                'ok' => false,
                'error' => 'Failed to query status',
            ], 500);
        }
    }

    public function logPrint(Request $request): JsonResponse
    {
        try {
            $guard = $this->authenticatedUserIdOrUnauthorized();
            if ($guard instanceof JsonResponse) {
                return $guard;
            }

            $validator = Validator::make($request->all(), [
                'user_ids' => ['nullable', 'array'],
                'user_ids.*' => ['integer', 'exists:users,id'],
                'student_ids' => ['nullable', 'array'],
                'student_ids.*' => ['integer', 'exists:students,id'],
                'school_year' => ['nullable', 'string', 'max:50'],
                'roles' => ['nullable', 'array'],
                'classes' => ['nullable', 'array'],
            ]);

            $validator->after(function ($validator): void {
                $data = $validator->getData();
                $users = array_values(array_filter((array) ($data['user_ids'] ?? []), static fn ($v) => (int) $v > 0));
                $students = array_values(array_filter((array) ($data['student_ids'] ?? []), static fn ($v) => (int) $v > 0));
                if ($users === [] && $students === []) {
                    $validator->errors()->add('user_ids', 'Provide at least one user id or student id.');
                }
            });

            if ($validator->fails()) {
                return response()->json([
                    'message' => 'Validation failed.',
                    'errors' => $validator->errors(),
                ], 422);
            }

            $data = $validator->validated();

            $inserted = $this->badgePrintLogService->log(
                userIds: array_values(array_unique(array_merge(
                    $data['user_ids'] ?? [],
                    $data['student_ids'] ?? []
                ))),
                actorId: $guard,
                schoolYear: $data['school_year'] ?? null,
                rolesMap: $data['roles'] ?? [],
                classesMap: $data['classes'] ?? []
            );

            return response()->json([
                'ok' => true,
                'inserted' => $inserted,
            ]);
        } catch (Throwable $e) {
            return response()->json([
                'ok' => false,
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    public function formData(Request $request): JsonResponse
    {
        return response()->json([
            'ok' => true,
            'data' => $this->badgeFormDataService->build(
                schoolYear: $request->input('school_year'),
                selectedUserIds: $this->parseStaffUserIds($request),
                selectedStudentIds: $this->parseStudentIds($request),
                activeRole: $request->input('active_role')
            ),
        ]);
    }

    /**
     * Staff user IDs for the badge picker (`users.id`).
     *
     * @return int[]
     */
    private function parseStaffUserIds(Request $request): array
    {
        $ids = $request->input('user_ids', $request->query('user_ids', []));

        if (is_string($ids)) {
            $ids = array_filter(array_map('trim', explode(',', $ids)), 'strlen');
        } elseif (!is_array($ids)) {
            $ids = $ids ? [$ids] : [];
        }

        $ids = array_values(array_unique(array_map(static fn ($v) => (int) $v, $ids)));

        return array_values(array_filter($ids, static fn ($v) => $v > 0));
    }

    /**
     * Combined `users.id` and `students.id` for print-status (same numeric space as stored in logs).
     *
     * @return int[]
     */
    private function parseCombinedBadgeIds(Request $request): array
    {
        return array_values(array_unique(array_merge(
            $this->parseStaffUserIds($request),
            $this->parseStudentIds($request)
        )));
    }

    /**
     * Student primary keys (`students.id`) from query or body.
     *
     * @return int[]
     */
    private function parseStudentIds(Request $request): array
    {
        $ids = $request->input('student_ids', $request->query('student_ids', []));

        if (is_string($ids)) {
            $ids = array_filter(array_map('trim', explode(',', $ids)), 'strlen');
        } elseif (!is_array($ids)) {
            $ids = $ids ? [$ids] : [];
        }

        $ids = array_values(array_unique(array_map(static fn ($v) => (int) $v, $ids)));

        return array_values(array_filter($ids, static fn ($v) => $v > 0));
    }

    /**
     * @return int|JsonResponse
     */
    private function authenticatedUserIdOrUnauthorized(): int|JsonResponse
    {
        $userId = (int) (auth()->id() ?? 0);
        if ($userId <= 0) {
            return response()->json(['ok' => false, 'message' => 'Unauthorized.'], 401);
        }

        return $userId;
    }
}
