<?php

namespace App\Http\Controllers\Api\PrintRequests;

use App\Http\Controllers\Api\Core\BaseApiController;
use App\Models\PrintRequest;
use App\Models\User;
use App\Services\PrintRequests\PrintRequestsPortalService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response;

class PrintRequestsController extends BaseApiController
{
    public function __construct(
        private PrintRequestsPortalService $portal,
    ) {
        parent::__construct();
    }

    public function teacher(): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        return $this->success($this->portal->teacherBootstrap($uid));
    }

    public function admin(): JsonResponse
    {
        return $this->success([
            'print_requests' => $this->portal->adminPrintRequests(),
        ]);
    }

    public function store(Request $request): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $validator = Validator::make($request->all(), [
            'file' => ['required', 'file', 'max:5120', 'mimes:pdf,jpg,jpeg,png,doc,docx,txt', 'mimetypes:application/pdf,image/jpeg,image/png,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
            'page_selection' => ['nullable', 'string', 'regex:/^\s*\d+(?:\s*-\s*\d+)?(?:\s*,\s*\d+(?:\s*-\s*\d+)?)*\s*$/'],
            'num_copies' => ['required', 'integer', 'min:1'],
            'required_by' => ['required', 'date'],
            'pickup_method' => ['required', 'string', 'max:255'],
            'class_id' => ['required', 'integer', 'min:1'],
        ]);

        if ($validator->fails()) {
            return $this->respondValidationError($validator->errors()->toArray());
        }

        /** @var \Illuminate\Http\UploadedFile $file */
        $file = $request->file('file');
        $model = $this->portal->storeTeacherUpload($validator->validated(), $file, $uid);

        return $this->respondCreated(
            ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())],
            'Print request created successfully.'
        );
    }

    public function teacherUpdate(Request $request, int $id): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $pr = PrintRequest::query()->find($id);
        if (! $pr) {
            return $this->error('Print request not found.', Response::HTTP_NOT_FOUND);
        }

        if (! $this->portal->teacherOwnsRequest($pr, $uid)) {
            return $this->error('You are not authorized to edit this request.', Response::HTTP_FORBIDDEN);
        }

        if (! $this->portal->teacherMayEditOrDelete($pr)) {
            return $this->error('Cannot edit a request that is already being processed.', Response::HTTP_BAD_REQUEST);
        }

        $rules = [
            'num_copies' => ['required', 'integer', 'min:1'],
            'required_by' => ['required', 'date'],
            'pickup_method' => ['required', 'string', Rule::in(['Self Pickup', 'Delivered to class'])],
            'page_selection' => ['nullable', 'string', 'regex:/^\s*\d+(?:\s*-\s*\d+)?(?:\s*,\s*\d+(?:\s*-\s*\d+)?)*\s*$/'],
        ];

        if ($request->hasFile('file') && $request->file('file')->isValid()) {
            $rules['file'] = ['required', 'file', 'max:5120', 'mimes:pdf,jpg,jpeg,png,doc,docx,txt', 'mimetypes:application/pdf,image/jpeg,image/png,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
        }

        $validator = Validator::make($request->all(), $rules);
        if ($validator->fails()) {
            return $this->respondValidationError($validator->errors()->toArray());
        }

        $data = $validator->validated();
        $newFile = ($request->hasFile('file') && $request->file('file')->isValid())
            ? $request->file('file')
            : null;

        $model = $this->portal->updateTeacherFields($pr, $data, $newFile);

        return $this->success(
            ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())],
            'Print request updated successfully.'
        );
    }

    public function adminStatus(Request $request, int $id): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $validator = Validator::make($request->all(), [
            'status' => ['required', 'string', Rule::in(['not_assigned', 'assigned', 'done', 'delivered'])],
        ]);
        if ($validator->fails()) {
            return $this->respondValidationError($validator->errors()->toArray());
        }

        $pr = PrintRequest::query()->find($id);
        if (! $pr) {
            return $this->error('Print request not found.', Response::HTTP_NOT_FOUND);
        }

        $newStatus = (string) $validator->validated()['status'];

        try {
            $model = $this->portal->applyAdminStatus($pr, $newStatus, $uid);
        } catch (\InvalidArgumentException $e) {
            return $this->error($e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        return $this->success(
            ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())],
            'Status updated successfully.'
        );
    }

    public function destroy(int $id): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $pr = PrintRequest::query()->find($id);
        if (! $pr) {
            return $this->error('Print request not found.', Response::HTTP_NOT_FOUND);
        }

        if (! $this->portal->teacherOwnsRequest($pr, $uid)) {
            return $this->error('You are not authorized to delete this request.', Response::HTTP_FORBIDDEN);
        }

        if (! $this->portal->teacherMayEditOrDelete($pr)) {
            return $this->error('Cannot delete a request that is already being processed.', Response::HTTP_BAD_REQUEST);
        }

        $this->portal->deleteTeacherRequest($pr);

        return $this->respondDeleted(null, 'Print request deleted successfully.');
    }

    public function copy(int $id): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $pr = PrintRequest::query()->find($id);
        if (! $pr) {
            return $this->error('Print request not found.', Response::HTTP_NOT_FOUND);
        }

        if (! $this->portal->teacherOwnsRequest($pr, $uid)) {
            return $this->error('You are not authorized to copy this request.', Response::HTTP_FORBIDDEN);
        }

        try {
            $model = $this->portal->copyFromExisting($pr, $uid);
        } catch (\InvalidArgumentException $e) {
            return $this->error($e->getMessage(), Response::HTTP_BAD_REQUEST);
        }

        return $this->respondCreated(
            ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())],
            'Print request copied successfully.'
        );
    }

    public function handCopy(Request $request): JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $validator = Validator::make($request->all(), [
            'num_copies' => ['required', 'integer', 'min:1'],
            'required_by' => ['required', 'date'],
            'pickup_method' => ['required', 'string', 'max:255'],
            'class_id' => ['required', 'integer', 'min:1'],
        ]);

        if ($validator->fails()) {
            return $this->respondValidationError($validator->errors()->toArray());
        }

        $model = $this->portal->storeHandCopy($validator->validated(), $uid);

        return $this->respondCreated(
            ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())],
            'Copy request submitted. Please hand the original document to the copy center.'
        );
    }

    public function download(int $id): BinaryFileResponse|JsonResponse
    {
        $uid = $this->getCurrentUserId();
        if (! $uid) {
            return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED);
        }

        $pr = PrintRequest::query()->find($id);
        if (! $pr) {
            return $this->error('Print request not found.', Response::HTTP_NOT_FOUND);
        }

        $isAdmin = $this->userHasPrintAdminRole(Auth::user());
        if (! $this->portal->authorizeDownload($pr, $uid, $isAdmin)) {
            return $this->error('Forbidden.', Response::HTTP_FORBIDDEN);
        }

        $path = $this->portal->absolutePathForDownload($pr);
        if ($path === null || ! is_readable($path)) {
            return $this->error('File not found.', Response::HTTP_NOT_FOUND);
        }

        $mime = @mime_content_type($path) ?: 'application/octet-stream';
        $safe = basename($path);

        return response()->download($path, $safe, [
            'Content-Type' => $mime,
        ]);
    }

    private function userHasPrintAdminRole(?User $user): bool
    {
        if (! $user) {
            return false;
        }

        $roles = $user->roles()
            ->pluck('roles.name')
            ->map(fn ($name) => strtolower((string) $name))
            ->toArray();

        foreach (['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'] as $allowed) {
            if (in_array($allowed, $roles, true)) {
                return true;
            }
        }

        return false;
    }
}
