package org.fuseri.modulelanguageschool.lecture;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import org.fuseri.model.dto.lecture.LectureCreateDto;
import org.fuseri.model.dto.lecture.LectureDto;
import org.fuseri.modulelanguageschool.ModuleLanguageSchoolApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * This class represents a RESTful controller for Lecture resources.
 * It handles incoming HTTP requests related to lectures, and delegates them to the appropriate service method.
 */
@Api(tags = "Lecture Controller")
@RestController
@RequestMapping("/lectures")
public class LectureController {

    private final LectureFacade lectureFacade;

    @Autowired
    public LectureController(LectureFacade lectureFacade) {
        this.lectureFacade = lectureFacade;
    }


    /**
     * Creates a new lecture resource by delegating to the LectureService's create method.
     *
     * @param lecture the LectureDto representing the lecture to be created
     * @return the LectureDto representing the newly created lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Create a new lecture")
    @PostMapping
    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "The lecture has been successfully created"),
            @ApiResponse(code = 400, message = "The request body is invalid")
    })
    public ResponseEntity<LectureDto> create(@Valid @RequestBody LectureCreateDto lecture) {
        LectureDto lectureDto = lectureFacade.create(lecture);
        return ResponseEntity.status(HttpStatus.CREATED).body(lectureDto);
    }

    /**
     * Retrieves a lecture resource by its ID.
     *
     * @param courseId the ID of the lecture to find
     * @return the LectureDto representing the found lecture
     */

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Retrieve a lecture by its ID")
    @GetMapping("find/{courseId}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been found"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> find(@PathVariable Long courseId) {
        LectureDto lectureDto = lectureFacade.findById(courseId);
        return ResponseEntity.ok(lectureDto);
    }

    /**
     * Retrieves a list of lectures for the corresponding course
     *
     * @param courseId the course to retrieve lectures from
     * @return the list of LectureDtos
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Retrieve a list of lectures for the corresponding course")
    @GetMapping("/findByCourse")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The list of lectures has been found"),
            @ApiResponse(code = 404, message = "The course with the specified ID does not exist")
    })
    public ResponseEntity<List<LectureDto>> findByCourse(@Valid @RequestParam Long courseId) {
        return ResponseEntity.ok(lectureFacade.findAll(courseId));
    }

    /**
     * Updates an existing lecture resource by delegating to the LectureService's update method.
     *
     * @param lecture the CourseCreateDto representing the updated lecture
     * @return the LectureDto representing the updated lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Update an existing lecture")
    @PutMapping("/update/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been successfully updated"),
            @ApiResponse(code = 400, message = "The request body is invalid"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> update(@PathVariable Long id, @Valid @RequestBody LectureCreateDto lecture) {
        return ResponseEntity.ok(lectureFacade.update(id, lecture));
    }

    /**
     * Deletes a lecture resource by its ID.
     *
     * @param id the ID of the lecture to delete
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Delete a lecture by its ID")
    @DeleteMapping("/delete/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "The lecture has been successfully deleted"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        lectureFacade.delete(id);
        return ResponseEntity.noContent().build();
    }


    /**
     * Adds lecturer to the existing lecture resource
     *
     * @param id         id of lecture to update
     * @param lecturerId UserDto for the course lecturer
     * @return the LectureDto representing the updated lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Add lecturer to the existing lecture")
    @PatchMapping("/setLecturer/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been successfully updated"),
            @ApiResponse(code = 400, message = "The request body is invalid"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> setLecturer(@PathVariable Long id, @RequestParam Long lecturerId) {
        return ResponseEntity.ok(lectureFacade.setLecturer(id, lecturerId));
    }

    /**
     * Adds student to the existing lecture resource
     *
     * @param id        id of lecture to update
     * @param studentId id for the course student
     * @return the LectureDto representing the updated lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Add student to the existing lecture")
    @PatchMapping("/enrolStudent/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been successfully updated"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> enrol(@PathVariable Long id, @RequestParam Long studentId) {
        return ResponseEntity.ok(lectureFacade.enrol(id, studentId));
    }

    /**
     * Adds student to the existing lecture resource
     *
     * @param id        id of lecture to update
     * @param principal http request received with user email
     * @return the LectureDto representing the updated lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Add me to the existing lecture")
    @PatchMapping("/enrol/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been successfully updated"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> enrol(@PathVariable Long id, @AuthenticationPrincipal OAuth2IntrospectionAuthenticatedPrincipal principal) throws JsonProcessingException {
        String email = principal.getSubject();
        return ResponseEntity.ok(lectureFacade.enrol(id, email));
    }

    /**
     * Removes student from the existing lecture resource
     *
     * @param id        id of lecture to update
     * @param studentId id for the course student
     * @return the LectureDto representing the updated lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Remove student from the existing lecture")
    @PatchMapping("/expelStudent/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been successfully updated"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> expel(@PathVariable Long id, @RequestParam Long studentId) {
        return ResponseEntity.ok(lectureFacade.expel(id, studentId));
    }

    /**
     * Removes student from the existing lecture resource
     *
     * @param id        id of lecture to update
     * @param principal http request received with user email
     * @return the LectureDto representing the updated lecture
     */
    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME))
    @ApiOperation(value = "Remove me from the existing lecture")
    @PatchMapping("/expel/{id}")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "The lecture has been successfully updated"),
            @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist")
    })
    public ResponseEntity<LectureDto> expel(@PathVariable Long id, @AuthenticationPrincipal OAuth2IntrospectionAuthenticatedPrincipal principal) throws JsonProcessingException {
        String email = principal.getSubject();
        return ResponseEntity.ok(lectureFacade.expel(id, email));
    }
}