package org.fuseri.moduleexercise.exercise;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import org.fuseri.model.dto.exercise.ExerciseCreateDto;
import org.fuseri.model.dto.exercise.ExerciseDto;
import org.fuseri.model.dto.exercise.QuestionDto;
import org.fuseri.moduleexercise.ModuleExerciseApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Represent a REST API controller for exercises
 * Handle HTTP requests related to exercises
 */
@RestController
@RequestMapping("/exercises")
public class ExerciseController {

    private final ExerciseFacade facade;

    /**
     * Constructor for ExerciseController
     *
     * @param facade the facade responsible for handling exercise-related logic
     */
    @Autowired
    public ExerciseController(ExerciseFacade facade) {
        this.facade = facade;
    }

    /**
     * Create a new exercise
     *
     * @param dto containing information about the exercise to create
     * @return a ResponseEntity containing an ExerciseDto object representing the newly created exercise
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Create an exercise", description = "Creates a new exercise.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "201", description = "Exercise created successfully."),
            @ApiResponse(responseCode = "400", description = "Invalid input.")
    })
    @PostMapping
    public ResponseEntity<ExerciseDto> create(@Valid @RequestBody ExerciseCreateDto dto) {
        ExerciseDto exerciseDto = facade.create(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(exerciseDto);
    }

    /**
     * Find an exercise by ID
     *
     * @param id the ID of the exercise to find
     * @return a ResponseEntity containing an ExerciseDto object representing the found exercise, or a 404 Not Found response
     * if the exercise with the specified ID was not found
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Get an exercise by ID", description = "Returns an exercise with the specified ID.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Exercise with the specified ID retrieved successfully."),
            @ApiResponse(responseCode = "404", description = "Exercise with the specified ID was not found.")
    })
    @GetMapping("/{id}")
    public ResponseEntity<ExerciseDto> find(@NotNull @PathVariable long id) {
        return ResponseEntity.ok(facade.find(id));
    }

    /**
     * Find exercises and return them in paginated format
     *
     * @param page the page number of the exercises to retrieve
     * @return A ResponseEntity containing paginated ExerciseDTOs.
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Get exercises in paginated format", description = "Returns exercises in paginated format.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successfully retrieved paginated exercises"),
            @ApiResponse(responseCode = "400", description = "Invalid page number supplied"),
    })
    @GetMapping
    public ResponseEntity<Page<ExerciseDto>> findAll(@PositiveOrZero @RequestParam int page) {
        return ResponseEntity.ok(facade.findAll(page));
    }

    /**
     * Find exercises that mach filters and return them in paginated format
     *
     * @param courseId   the id of the course to filter by
     * @param difficulty the difficulty level to filter by
     * @param page       the page number of the exercises to retrieve
     * @return A ResponseEntity containing filtered and paginated ExerciseDTOs
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Filter exercises per difficulty and per course", description = "Returns exercises which belong to specified course and have specified difficulty.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successfully retrieved filtered paginated exercises."),
    })
    @GetMapping("filter")
    public ResponseEntity<Page<ExerciseDto>> findPerDifficultyPerCourse(
            @PositiveOrZero @RequestParam int page, @NotNull @RequestParam long courseId,
            @PositiveOrZero @RequestParam int difficulty) {
        Page<ExerciseDto> exercises = facade.findByCourseIdAndDifficulty(courseId, difficulty, page);
        return ResponseEntity.ok(exercises);
    }

    /**
     * Find questions by exercise ID and return them in a paginated format
     *
     * @param exerciseId the ID of the exercise to find questions for
     * @param page       the page number of the questions to retrieve
     * @return a ResponseEntity containing paginated QuestionDTOs which belong to an exercise with exerciseId
     * or a NOT_FOUND response if the exercise ID is invalid
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Find questions belonging to exercise by exercise ID",
            description = "Returns a paginated list of questions for the specified exercise ID.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Questions found and returned successfully."),
            @ApiResponse(responseCode = "404", description = "Exercise with the specified ID was not found.")
    })
    @GetMapping("/{exercise-id}/questions")
    public ResponseEntity<Page<QuestionDto>> findQuestions(@NotNull @PathVariable("exercise-id") long exerciseId,
                                                           @PositiveOrZero @RequestParam int page) {
        Page<QuestionDto> questions = facade.getQuestions(exerciseId, page);
        return ResponseEntity.ok(questions);
    }

    /**
     * Update an exercise with ID
     *
     * @param id  the ID of the exercise to update
     * @param dto the ExerciseCreateDto object containing information about the exercise to update
     * @return A ResponseEntity with an ExerciseDto object representing the updated exercise an HTTP status code of 200 if the update was successful.
     * or a NOT_FOUND response if the exercise ID is invalid
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Update a exercise", description = "Updates a exercise with the specified ID.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Exercise with the specified ID updated successfully."),
            @ApiResponse(responseCode = "400", description = "Invalid input."),
            @ApiResponse(responseCode = "404", description = "Exercise with the specified ID was not found.")
    })
    @PutMapping("/{id}")
    public ResponseEntity<ExerciseDto> update(@NotNull @PathVariable long id, @Valid @RequestBody ExerciseCreateDto dto) {
        return ResponseEntity.ok(facade.update(id, dto));
    }

    /**
     * Delete an exercise with ID
     *
     * @param id the ID of the exercise to delete
     */
    @Operation(security = @SecurityRequirement(name = ModuleExerciseApplication.SECURITY_SCHEME_NAME),
            summary = "Delete a exercise with specified ID", description = "Deletes a exercise with the specified ID.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "204", description = "Exercise with the specified ID deleted successfully."),
    })
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@NotNull @PathVariable long id) {
        facade.delete(id);
        return ResponseEntity.noContent().build();
    }

}
