package cz.muni.fi.pa165.core.user;

import cz.muni.fi.pa165.core.helpers.exceptions.EntityConflictException;
import cz.muni.fi.pa165.core.helpers.exceptions.EntityDeletionException;
import cz.muni.fi.pa165.model.dto.common.Result;
import cz.muni.fi.pa165.model.dto.house.HouseDto;
import cz.muni.fi.pa165.model.dto.role.enums.RoleTypeEnum;
import cz.muni.fi.pa165.model.dto.user.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.tags.Tag;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.PositiveOrZero;
import org.h2.engine.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;

@RestController
@RequestMapping("/api/user")
@Tag(name = "User", description = "User API")
public class UserController {
	private final UserFacade userFacade;
	private final UserService userService; // TODO: this could be done in UserFacade

	@Autowired
	public UserController(UserFacade userFacade, UserService userService) {
		this.userFacade = userFacade;
		this.userService = userService;
	}

	@Operation(
			summary = "Find user by ID",
			description = "Returns a single user")
	@ApiResponses(
			value = {
					@ApiResponse(
							responseCode = "200",
							description = "User found",
							content = {
									@Content(
											mediaType = "application/json",
											schema = @Schema(implementation = UserDto.class))
							}),
					@ApiResponse(responseCode = "404", description = "User not found", content = @Content)
			})
	@GetMapping("/{id}")
	public ResponseEntity<?> findById(
			@Parameter(description = "ID of user to be searched") @PathVariable String id) {
		try {
			return ResponseEntity.ok(userFacade.findById(id));
		} catch (EntityNotFoundException ex) {
			return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
		}

	}

	@Operation(
			summary = "Create user",
			description = "Creates a new user")
	@ApiResponses(
			value = {
					@ApiResponse(
							responseCode = "201",
							description = "User created",
							content = {
									@Content(
											mediaType = "application/json",
											schema = @Schema(implementation = UserDto.class))
							}),
					@ApiResponse(
							responseCode = "409",
							description = "User with the same name already exists",
							content = @Content)
			})
	@PostMapping
	public ResponseEntity<?> create(
			@Parameter(description = "User to be created") @RequestBody UserCreateDto userCreateDto) {
		try {
			return ResponseEntity.status(HttpStatus.CREATED).body(userFacade.create(userCreateDto));
		} catch (EntityConflictException ex) {
			return ResponseEntity.status(HttpStatus.CONFLICT)
					.body(ex.getMessage());

		}
	}

	@Operation(
			summary = "Get all users",
			description = "Returns all users")
	@ApiResponses(
			value = {
					@ApiResponse(
							responseCode = "200",
							description = "Users found",
							content = {
									@Content(
											mediaType = "application/json",
											schema = @Schema(implementation = Result.class))
							})
			})
	@GetMapping
	public Result<UserDto> findAll(@Parameter(description = "Page number of results to retrieve") @RequestParam(defaultValue = "0", required = false) @PositiveOrZero int page,
								   @Parameter(description = "Page size of results to retrieve") @RequestParam(defaultValue = "-1", required = false) int pageSize) {
		if (pageSize < 0)
			pageSize = 10;
		Pageable pageWithElements = PageRequest.of(page, pageSize);
		System.out.println(pageWithElements.getClass());
		return userFacade.findAllPageable(pageWithElements);
	}

	@PutMapping("/{id}")
	@Operation(summary = "Update a user by ID")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "User updated successfully"),
			@ApiResponse(responseCode = "400", description = "Invalid input"),
			@ApiResponse(responseCode = "404", description = "User not found"),
			@ApiResponse(responseCode = "409", description = "User (with username) already exists"),
			@ApiResponse(responseCode = "500", description = "Internal server error")
	})
	public ResponseEntity<?> updateById(
			@Parameter(description = "ID of the user to be updated") @PathVariable String id,
			@RequestBody @Valid UserUpdateDto userUpdateDto) {
		try {
			return ResponseEntity.status(HttpStatus.OK).body(userFacade.updateById(userUpdateDto, id));
		} catch (EntityConflictException ex) {
			return ResponseEntity.status(HttpStatus.CONFLICT)
					.body(ex.getMessage());
		} catch (EntityNotFoundException ex) {
			return ResponseEntity.status(HttpStatus.NOT_FOUND)
					.body(ex.getMessage());
		}
	}

	@DeleteMapping("/houseUser/{id}")
	@Operation(summary = "Delete a house user by ID")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "User deleted successfully"),
			@ApiResponse(responseCode = "404", description = "User not found"),
			@ApiResponse(responseCode = "500", description = "Internal server error")
	})
	public ResponseEntity<?> deleteByIdHouseUser(
			@Parameter(description = "ID of the user to be deleted") @PathVariable String id) {
		try {
			return ResponseEntity.status(HttpStatus.OK).body(userFacade.deleteHouseUserById(id));
		} catch (EntityNotFoundException ex) {
			return ResponseEntity.status(HttpStatus.NOT_FOUND)
					.body(ex.getMessage());

		} catch (EntityDeletionException ex) {
			return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY)
					.body(ex.getMessage());
		}
	}

	@DeleteMapping("/{id}")
	@Operation(summary = "Delete a user by ID")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "User deleted successfully"),
			@ApiResponse(responseCode = "404", description = "User not found"),
			@ApiResponse(responseCode = "500", description = "Internal server error")
	})
	public ResponseEntity<?> deleteById(
			@Parameter(description = "ID of the user to be deleted") @PathVariable String id) {
		try {
			return ResponseEntity.status(HttpStatus.OK).body(userFacade.deleteById(id));
		} catch (EntityNotFoundException ex) {
			return ResponseEntity.status(HttpStatus.NOT_FOUND)
					.body(ex.getMessage());

		} catch (EntityDeletionException ex) {
			return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY)
					.body(ex.getMessage());
		}
	}

	@PostMapping("/statistics")
	@Operation(summary = "Send a user house with data")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Success"),
			@ApiResponse(responseCode = "404", description = "User not found"),
	})
	public Double userHouseStatistics(
			@RequestBody UserStatisticsCreateDto createStatDto) {
		return userService.getConsumption(createStatDto.getUserId(), createStatDto.getHouseId(),
				createStatDto.getStartTime(), createStatDto.getEndTime());
	}

	@Operation(summary = "Log in a user")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Successful login"),
			@ApiResponse(responseCode = "401", description = "Invalid credentials")
	})
	@PostMapping("/login")
	public ResponseEntity<?> login(@RequestBody LoginInfoDto request) {
		return userFacade.login(request);
	}

	@Operation(summary = "Log out a user")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Successful logout"),
			@ApiResponse(responseCode = "401", description = "Unauthorized")
	})
	@PostMapping("/logout")
	public ResponseEntity<?> logout() {
		return userFacade.logout();
	}

	@PutMapping("/password")
	@Operation(summary = "Change user password",
			description = "Change the password for the logged user."
	)
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Password changed successfully."),
			@ApiResponse(responseCode = "400", description = "Bad request.", content = @Content),
			@ApiResponse(responseCode = "401", description = "Unauthorized.", content = @Content),
			@ApiResponse(responseCode = "404", description = "User not found.", content = @Content),
			@ApiResponse(responseCode = "500", description = "Internal server error.", content = @Content)
	})
	public ResponseEntity<?> changePassword(
			@Parameter(description = "The old and new password information.", required = true)
			@Valid @RequestBody ChangePasswordDto request) {
		final String userId = "00000";
		return userFacade.changePassword(request, userId); // userId from JWT token or session cookie.
	}

	@Operation(
			summary = "Get all employees",
			description = "Returns all employees")
	@ApiResponses(
			value = {
					@ApiResponse(
							responseCode = "200",
							description = "Employees found",
							content = {
									@Content(
											mediaType = "application/json",
											schema = @Schema(implementation = UserDto.class))
							})
			})
	@GetMapping("/employees")
	public List<UserDto> findAllEmployees() {
		return userFacade.findAllUsersByRole(RoleTypeEnum.CompanyRole);
	}

	@Operation(
			summary = "Get all customers",
			description = "Returns all customers")
	@ApiResponses(
			value = {
					@ApiResponse(
							responseCode = "200",
							description = "Customers found",
							content = {
									@Content(
											mediaType = "application/json",
											schema = @Schema(implementation = UserDto.class))
							})
			})
	@GetMapping("/customers")
	public List<UserDto> findAllCustomers() {
		return userFacade.findAllUsersByRole(RoleTypeEnum.HouseRole);
	}

	@Operation(
			summary = "Find houses of user by ID",
			description = "Returns houses of user")
	@ApiResponses(
			value = {
					@ApiResponse(
							responseCode = "200",
							description = "User found",
							content = {
									@Content(
											mediaType = "application/json",
											schema = @Schema(implementation = HouseDto.class))
							}),
					@ApiResponse(responseCode = "404", description = "User not found", content = @Content)
			})
	@GetMapping("houses/{id}")
	public List<HouseDto> findHousesById(
			@Parameter(description = "ID of user to be searched") @PathVariable String id) {
		try {
			return userFacade.findHousesByUserId(id);
		} catch (EntityNotFoundException ex) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage());
		}

	}
}