package de.effigenix.infrastructure.inventory.web.controller;

import de.effigenix.application.inventory.ActivateStorageLocation;
import de.effigenix.application.inventory.CreateStorageLocation;
import de.effigenix.application.inventory.DeactivateStorageLocation;
import de.effigenix.application.inventory.GetStorageLocation;
import de.effigenix.application.inventory.ListStorageLocations;
import de.effigenix.application.inventory.UpdateStorageLocation;
import de.effigenix.application.inventory.command.CreateStorageLocationCommand;
import de.effigenix.application.inventory.command.UpdateStorageLocationCommand;
import de.effigenix.domain.inventory.StorageLocationError;
import de.effigenix.infrastructure.inventory.web.dto.CreateStorageLocationRequest;
import de.effigenix.infrastructure.inventory.web.dto.StorageLocationResponse;
import de.effigenix.infrastructure.inventory.web.dto.UpdateStorageLocationRequest;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.security.ActorId;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/inventory/storage-locations")
@SecurityRequirement(name = "Bearer Authentication")
@Tag(name = "Storage Locations", description = "Storage location management endpoints")
public class StorageLocationController {

    private static final Logger logger = LoggerFactory.getLogger(StorageLocationController.class);

    private final CreateStorageLocation createStorageLocation;
    private final GetStorageLocation getStorageLocation;
    private final UpdateStorageLocation updateStorageLocation;
    private final DeactivateStorageLocation deactivateStorageLocation;
    private final ActivateStorageLocation activateStorageLocation;
    private final ListStorageLocations listStorageLocations;

    public StorageLocationController(
            CreateStorageLocation createStorageLocation,
            GetStorageLocation getStorageLocation,
            UpdateStorageLocation updateStorageLocation,
            DeactivateStorageLocation deactivateStorageLocation,
            ActivateStorageLocation activateStorageLocation,
            ListStorageLocations listStorageLocations
    ) {
        this.createStorageLocation = createStorageLocation;
        this.getStorageLocation = getStorageLocation;
        this.updateStorageLocation = updateStorageLocation;
        this.deactivateStorageLocation = deactivateStorageLocation;
        this.activateStorageLocation = activateStorageLocation;
        this.listStorageLocations = listStorageLocations;
    }

    @GetMapping
    @PreAuthorize("hasAuthority('STOCK_READ') or hasAuthority('STOCK_WRITE')")
    public ResponseEntity<List<StorageLocationResponse>> listStorageLocations(
            @RequestParam(value = "storageType", required = false) String storageType,
            @RequestParam(value = "active", required = false) Boolean active
    ) {
        return switch (listStorageLocations.execute(storageType, active)) {
            case Result.Failure(var err) -> throw new StorageLocationDomainErrorException(err);
            case Result.Success(var locations) -> {
                var response = locations.stream()
                        .map(StorageLocationResponse::from)
                        .toList();
                yield ResponseEntity.ok(response);
            }
        };
    }

    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('STOCK_READ') or hasAuthority('STOCK_WRITE')")
    public ResponseEntity<StorageLocationResponse> getStorageLocation(
            @PathVariable("id") String storageLocationId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        return switch (getStorageLocation.execute(storageLocationId, actorId)) {
            case Result.Failure(var err) -> throw new StorageLocationDomainErrorException(err);
            case Result.Success(var location) -> ResponseEntity.ok(StorageLocationResponse.from(location));
        };
    }

    @PostMapping
    @PreAuthorize("hasAuthority('STOCK_WRITE')")
    public ResponseEntity<StorageLocationResponse> createStorageLocation(
            @Valid @RequestBody CreateStorageLocationRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Creating storage location: {} by actor: {}", request.name(), actorId.value());

        var cmd = new CreateStorageLocationCommand(
                request.name(), request.storageType(),
                request.minTemperature(), request.maxTemperature()
        );
        return switch (createStorageLocation.execute(cmd, actorId)) {
            case Result.Failure(var err) -> throw new StorageLocationDomainErrorException(err);
            case Result.Success(var location) -> {
                logger.info("Storage location created: {}", request.name());
                yield ResponseEntity.status(HttpStatus.CREATED)
                        .body(StorageLocationResponse.from(location));
            }
        };
    }

    @PutMapping("/{id}")
    @PreAuthorize("hasAuthority('STOCK_WRITE')")
    public ResponseEntity<StorageLocationResponse> updateStorageLocation(
            @PathVariable("id") String storageLocationId,
            @RequestBody UpdateStorageLocationRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Updating storage location: {} by actor: {}", storageLocationId, actorId.value());

        var cmd = new UpdateStorageLocationCommand(
                storageLocationId, request.name(),
                request.minTemperature(), request.maxTemperature()
        );
        return switch (updateStorageLocation.execute(cmd, actorId)) {
            case Result.Failure(var err) -> throw new StorageLocationDomainErrorException(err);
            case Result.Success(var location) -> {
                logger.info("Storage location updated: {}", storageLocationId);
                yield ResponseEntity.ok(StorageLocationResponse.from(location));
            }
        };
    }

    @PatchMapping("/{id}/deactivate")
    @PreAuthorize("hasAuthority('STOCK_WRITE')")
    public ResponseEntity<StorageLocationResponse> deactivateStorageLocation(
            @PathVariable("id") String storageLocationId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Deactivating storage location: {} by actor: {}", storageLocationId, actorId.value());

        return switch (deactivateStorageLocation.execute(storageLocationId, actorId)) {
            case Result.Failure(var err) -> throw new StorageLocationDomainErrorException(err);
            case Result.Success(var location) -> {
                logger.info("Storage location deactivated: {}", storageLocationId);
                yield ResponseEntity.ok(StorageLocationResponse.from(location));
            }
        };
    }

    @PatchMapping("/{id}/activate")
    @PreAuthorize("hasAuthority('STOCK_WRITE')")
    public ResponseEntity<StorageLocationResponse> activateStorageLocation(
            @PathVariable("id") String storageLocationId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Activating storage location: {} by actor: {}", storageLocationId, actorId.value());

        return switch (activateStorageLocation.execute(storageLocationId, actorId)) {
            case Result.Failure(var err) -> throw new StorageLocationDomainErrorException(err);
            case Result.Success(var location) -> {
                logger.info("Storage location activated: {}", storageLocationId);
                yield ResponseEntity.ok(StorageLocationResponse.from(location));
            }
        };
    }

    private ActorId extractActorId(Authentication authentication) {
        if (authentication == null || authentication.getName() == null) {
            throw new IllegalStateException("No authentication found in SecurityContext");
        }
        return ActorId.of(authentication.getName());
    }

    public static class StorageLocationDomainErrorException extends RuntimeException {
        private final StorageLocationError error;

        public StorageLocationDomainErrorException(StorageLocationError error) {
            super(error.message());
            this.error = error;
        }

        public StorageLocationError getError() {
            return error;
        }
    }
}
