package org.fuseri.modulelanguageschool.user;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
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.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import org.fuseri.model.dto.course.CourseDto;
import org.fuseri.model.dto.user.UserAddLanguageDto;
import org.fuseri.model.dto.user.UserCreateDto;
import org.fuseri.model.dto.user.UserDto;
import org.fuseri.modulelanguageschool.ModuleLanguageSchoolApplication;
import org.fuseri.modulelanguageschool.exceptions.EmailMismatchException;
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.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
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;

import java.util.List;
import java.util.Objects;

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserFacade facade;

    @Autowired
    public UserController(UserFacade facade) {
        this.facade = facade;
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)
            , summary = "Get a user by Id", description = "Returns a user with specified Id")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "User with the specified Id is retrieved Successfuly",
                    content = @Content(schema = @Schema(implementation = UserDto.class)
                    )),
            @ApiResponse(responseCode = "404", description = "User with the specified ID was not found.")
    })
    @GetMapping("/{id}")
    public ResponseEntity<UserDto> find(@PathVariable @NotNull Long id) {
        try {
            return ResponseEntity.ok(facade.find(id));
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)
            , summary = "Create a User", description = "Creates a new User.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "201", description = "User created successfully."),
            @ApiResponse(responseCode = "400", description = "Invalid input.")
    })
    @PostMapping
    public ResponseEntity<UserDto> create(@Valid @RequestBody UserCreateDto dto) {
        UserDto user = facade.create(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(user);
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)
            , summary = "Delete a User with specified ID", description = "Deletes a User with the specified ID.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "204", description = "User with the specified ID deleted successfully."),
    })
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable @NotNull Long id) {
        facade.delete(id);
        return ResponseEntity.noContent().build();
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)
            , summary = "Update a User", description = "Updates a User with the specified ID.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "User with the specified ID updated successfully."),
            @ApiResponse(responseCode = "400", description = "Invalid input."),
            @ApiResponse(responseCode = "404", description = "User with the specified ID was not found.")
    })
    @PutMapping("/{id}")
    public ResponseEntity<UserDto> update(@NotNull @PathVariable Long id, @Valid @RequestBody UserCreateDto dto) {
        try {
            return ResponseEntity.ok(facade.update(id, dto));
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME),
            summary = "Get Users in paginated format", description = "Returns Users in paginated format.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successfully retrieved paginated Users"),
            @ApiResponse(responseCode = "400", description = "Invalid page number supplied"),
    })
    @GetMapping
    public ResponseEntity<Page<UserDto>> findAll(@PositiveOrZero @NotNull @RequestParam int page) {
        var a = facade.findAll(page);
        return ResponseEntity.ok(a);
    }


    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {}),
            summary = "Registers a new user after oath login", description = "Saves a new user into the database after oath login.")
    @PostMapping("/register")
    public ResponseEntity<UserDto> register(@RequestBody @Valid UserCreateDto dto,
                                            @AuthenticationPrincipal OAuth2IntrospectionAuthenticatedPrincipal principal) {
        if (!Objects.equals(principal.getSubject(), dto.getEmail())) {
            throw new EmailMismatchException(
                    String.format("Token email %s and body email %s does not match.", principal.getSubject(), dto.getEmail()));
        }

        UserDto user = facade.register(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(user);
    }


    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME),
            summary = "get finished courses", description = "retrieves finished courses of user with given Id")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successfully retrieved finished courses"),
            @ApiResponse(responseCode = "400", description = "Invalid input")
    })
    @GetMapping("/{id}/finished-courses")
    public ResponseEntity<List<CourseDto>> getFinished(@PathVariable @NotNull Long id) {
        return ResponseEntity.ok(facade.getFinished(id));
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME),
            summary = "get enrolled courses", description = "retrieves currently enrolled courses of user with given Id")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successfully retrieved enrolled courses"),
            @ApiResponse(responseCode = "400", description = "Invalid input")
    })
    @GetMapping("/{id}/courses")
    public ResponseEntity<List<CourseDto>> getEnrolled(@PathVariable @NotNull Long id) {
        return ResponseEntity.ok(facade.getEnrolled(id));
    }

    @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME),
            summary = "adds a language", description = "adds a new language and proficiency to user")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successfully added a language"),
            @ApiResponse(responseCode = "404", description = "User with given Id does not exist"),
            @ApiResponse(responseCode = "400", description = "Invalid input")
    })
    @PutMapping("/{id}/languages")
    public ResponseEntity<UserDto> addLanguage(@PathVariable @NotNull Long id, @Valid @RequestBody UserAddLanguageDto body) {
        try {
            return ResponseEntity.ok(facade.addLanguageProficiency(id, body));
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }

}
