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

import de.effigenix.application.inventory.GetStockMovement;
import de.effigenix.application.inventory.ListStockMovements;
import de.effigenix.application.inventory.RecordStockMovement;
import de.effigenix.application.inventory.command.RecordStockMovementCommand;
import de.effigenix.domain.inventory.StockMovementError;
import de.effigenix.infrastructure.inventory.web.dto.RecordStockMovementRequest;
import de.effigenix.infrastructure.inventory.web.dto.StockMovementResponse;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.security.ActorId;
import io.swagger.v3.oas.annotations.Operation;
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.format.annotation.DateTimeFormat;
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.time.Instant;
import java.util.List;

@RestController
@RequestMapping("/api/inventory/stock-movements")
@SecurityRequirement(name = "Bearer Authentication")
@Tag(name = "Stock Movements", description = "Stock movement tracking endpoints")
public class StockMovementController {

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

    private final RecordStockMovement recordStockMovement;
    private final GetStockMovement getStockMovement;
    private final ListStockMovements listStockMovements;

    public StockMovementController(RecordStockMovement recordStockMovement,
                                   GetStockMovement getStockMovement,
                                   ListStockMovements listStockMovements) {
        this.recordStockMovement = recordStockMovement;
        this.getStockMovement = getStockMovement;
        this.listStockMovements = listStockMovements;
    }

    @PostMapping
    @PreAuthorize("hasAuthority('STOCK_MOVEMENT_WRITE')")
    public ResponseEntity<StockMovementResponse> recordMovement(
            @Valid @RequestBody RecordStockMovementRequest request,
            Authentication authentication
    ) {
        logger.info("Recording stock movement type: {} for stock: {} by actor: {}",
                request.movementType(), request.stockId(), authentication.getName());

        var cmd = new RecordStockMovementCommand(
                request.stockId(), request.articleId(), request.stockBatchId(),
                request.batchId(), request.batchType(),
                request.movementType(), request.direction(),
                request.quantityAmount(), request.quantityUnit(),
                request.reason(), request.referenceDocumentId(),
                authentication.getName()
        );
        return switch (recordStockMovement.execute(cmd, ActorId.of(authentication.getName()))) {
            case Result.Failure(var err) -> throw new StockMovementDomainErrorException(err);
            case Result.Success(var movement) -> {
                logger.info("Stock movement recorded: {}", movement.id().value());
                yield ResponseEntity.status(HttpStatus.CREATED)
                        .body(StockMovementResponse.from(movement));
            }
        };
    }

    @GetMapping
    @PreAuthorize("hasAuthority('STOCK_MOVEMENT_READ')")
    @Operation(summary = "List stock movements",
            description = "Filter priority (only one filter applied): stockId > articleId > batchReference > movementType > from/to")
    public ResponseEntity<List<StockMovementResponse>> listMovements(
            @RequestParam(required = false) String stockId,
            @RequestParam(required = false) String articleId,
            @RequestParam(required = false) String movementType,
            @RequestParam(required = false) String batchReference,
            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant from,
            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant to,
            Authentication authentication
    ) {
        return switch (listStockMovements.execute(stockId, articleId, movementType,
                batchReference, from, to, ActorId.of(authentication.getName()))) {
            case Result.Failure(var err) -> throw new StockMovementDomainErrorException(err);
            case Result.Success(var movements) -> {
                var responses = movements.stream()
                        .map(StockMovementResponse::from)
                        .toList();
                yield ResponseEntity.ok(responses);
            }
        };
    }

    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('STOCK_MOVEMENT_READ')")
    public ResponseEntity<StockMovementResponse> getMovement(@PathVariable String id,
                                                               Authentication authentication) {
        return switch (getStockMovement.execute(id, ActorId.of(authentication.getName()))) {
            case Result.Failure(var err) -> throw new StockMovementDomainErrorException(err);
            case Result.Success(var movement) -> ResponseEntity.ok(StockMovementResponse.from(movement));
        };
    }

    public static class StockMovementDomainErrorException extends RuntimeException {
        private final StockMovementError error;

        public StockMovementDomainErrorException(StockMovementError error) {
            super(error.message());
            this.error = error;
        }

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