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

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.device.DeviceCreateDto;
import cz.muni.fi.pa165.model.dto.device.DeviceDto;
import cz.muni.fi.pa165.model.dto.device.DeviceUpdateDto;
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.tags.Tag;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.PositiveOrZero;
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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

@RestController
@RequestMapping("/api/device")
@Tag(name = "Device", description = "Device API")
public class DeviceController {

	private final DeviceFacade deviceFacade;

	@Autowired
	public DeviceController(DeviceFacade deviceFacade) {
		this.deviceFacade = deviceFacade;
	}

	@Operation(summary = "Find all devices with pagination or without", responses = {
			@ApiResponse(responseCode = "200", description = "List of devices", content = {
					@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = Result.class))
			})
	})
	@GetMapping
	public Result<DeviceDto> 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 deviceFacade.findAllPageable(pageWithElements);
	}

	@Operation(
			summary = "Get device by id",
			description = "Returns a device with the specified id.")
	@ApiResponse(responseCode = "200", description = "Device found", content = {
			@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = DeviceDto.class))
	})
	@ApiResponse(responseCode = "404", description = "Device not found", content = @Content)
	@GetMapping("/{id}")
	public DeviceDto findById(@PathVariable @Parameter(description = "The id of the device.") String id) {
		try {
			return deviceFacade.findById(id);
		} catch (EntityNotFoundException ex) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage());
		}
	}

	@Operation(
			summary = "Create device",
			description = "Creates a new device.")
	@ApiResponse(responseCode = "201", description = "The newly created device",
			content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
					schema = @Schema(implementation = DeviceDto.class)))
	@ApiResponse(responseCode = "404", description = "Company not found", content = @Content)
	@ApiResponse(responseCode = "409", description = "Device with the same name already exists", content = @Content)
	@PostMapping
	public ResponseEntity<DeviceDto> create(@RequestBody DeviceCreateDto deviceCreateDto) {
		try {
			return ResponseEntity.status(HttpStatus.CREATED).body(deviceFacade.create(deviceCreateDto));
		} catch (EntityConflictException ex) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, ex.getMessage());
		} catch (EntityNotFoundException ex) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage());
		}
	}

	@Operation(
			summary = "Update device",
			description = "Updates the device with the specified id.")
	@ApiResponse(responseCode = "201", description = "The updated device",
			content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
					schema = @Schema(implementation = DeviceDto.class)))
	@ApiResponse(responseCode = "404", description = "Device/Company not found", content = @Content)
	@ApiResponse(responseCode = "409", description = "Device with the same name already exists", content = @Content)
	@PutMapping("/{id}")
	public DeviceDto updateById(@PathVariable @Parameter(description = "The id of the device.") String id,
								@RequestBody @Valid DeviceUpdateDto deviceUpdateDto) {
		try {
			return deviceFacade.updateById(deviceUpdateDto, id);
		} catch (EntityConflictException ex) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, ex.getMessage());
		}
	}

	@Operation(
			summary = "Delete device",
			description = "Deletes the device with the specified id.")
	@ApiResponse(responseCode = "200", description = "The deleted device",
			content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
					schema = @Schema(implementation = DeviceDto.class)))
	@ApiResponse(responseCode = "404", description = "Device not found", content = @Content)
	@ApiResponse(responseCode = "422", description = "Device could not be deleted", content = @Content)
	@ApiResponse(responseCode = "505", description = "Error occurred", content = @Content)
	@DeleteMapping("/{id}")
	public ResponseEntity<?> deleteById(
			@PathVariable @Parameter(description = "The id of the device.") String id) {
		try {
			return ResponseEntity.status(HttpStatus.OK).body(deviceFacade.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());
		} catch (Exception e) {
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
					.body("Error occurred");
		}
	}
}
