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

import de.effigenix.application.production.CancelProductionOrder;
import de.effigenix.application.production.CompleteProductionOrder;
import de.effigenix.application.production.CreateProductionOrder;
import de.effigenix.application.production.ReleaseProductionOrder;
import de.effigenix.application.production.StartProductionOrder;
import de.effigenix.application.production.command.CancelProductionOrderCommand;
import de.effigenix.application.production.command.CompleteProductionOrderCommand;
import de.effigenix.application.production.command.CreateProductionOrderCommand;
import de.effigenix.application.production.command.ReleaseProductionOrderCommand;
import de.effigenix.application.production.command.StartProductionOrderCommand;
import de.effigenix.domain.production.ProductionOrderError;
import de.effigenix.infrastructure.production.web.dto.CancelProductionOrderRequest;
import de.effigenix.infrastructure.production.web.dto.CreateProductionOrderRequest;
import de.effigenix.infrastructure.production.web.dto.ProductionOrderResponse;
import de.effigenix.infrastructure.production.web.dto.StartProductionOrderRequest;
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.*;

@RestController
@RequestMapping("/api/production/production-orders")
@SecurityRequirement(name = "Bearer Authentication")
@Tag(name = "Production Orders", description = "Production order management endpoints")
public class ProductionOrderController {

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

    private final CreateProductionOrder createProductionOrder;
    private final ReleaseProductionOrder releaseProductionOrder;
    private final StartProductionOrder startProductionOrder;
    private final CompleteProductionOrder completeProductionOrder;
    private final CancelProductionOrder cancelProductionOrder;

    public ProductionOrderController(CreateProductionOrder createProductionOrder,
                                     ReleaseProductionOrder releaseProductionOrder,
                                     StartProductionOrder startProductionOrder,
                                     CompleteProductionOrder completeProductionOrder,
                                     CancelProductionOrder cancelProductionOrder) {
        this.createProductionOrder = createProductionOrder;
        this.releaseProductionOrder = releaseProductionOrder;
        this.startProductionOrder = startProductionOrder;
        this.completeProductionOrder = completeProductionOrder;
        this.cancelProductionOrder = cancelProductionOrder;
    }

    @PostMapping
    @PreAuthorize("hasAuthority('PRODUCTION_ORDER_WRITE')")
    public ResponseEntity<ProductionOrderResponse> createProductionOrder(
            @Valid @RequestBody CreateProductionOrderRequest request,
            Authentication authentication
    ) {
        logger.info("Creating production order for recipe: {} by actor: {}", request.recipeId(), authentication.getName());

        var cmd = new CreateProductionOrderCommand(
                request.recipeId(),
                request.plannedQuantity(),
                request.plannedQuantityUnit(),
                request.plannedDate(),
                request.priority(),
                request.notes()
        );

        var result = createProductionOrder.execute(cmd, ActorId.of(authentication.getName()));

        if (result.isFailure()) {
            throw new ProductionOrderDomainErrorException(result.unsafeGetError());
        }

        return ResponseEntity.status(HttpStatus.CREATED)
                .body(ProductionOrderResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/release")
    @PreAuthorize("hasAuthority('PRODUCTION_ORDER_WRITE')")
    public ResponseEntity<ProductionOrderResponse> releaseProductionOrder(
            @PathVariable String id,
            Authentication authentication
    ) {
        logger.info("Releasing production order: {} by actor: {}", id, authentication.getName());

        var cmd = new ReleaseProductionOrderCommand(id);
        var result = releaseProductionOrder.execute(cmd, ActorId.of(authentication.getName()));

        if (result.isFailure()) {
            throw new ProductionOrderDomainErrorException(result.unsafeGetError());
        }

        return ResponseEntity.ok(ProductionOrderResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/start")
    @PreAuthorize("hasAuthority('PRODUCTION_ORDER_WRITE')")
    public ResponseEntity<ProductionOrderResponse> startProductionOrder(
            @PathVariable String id,
            @Valid @RequestBody StartProductionOrderRequest request,
            Authentication authentication
    ) {
        logger.info("Starting production for order: {} with batch: {} by actor: {}", id, request.batchId(), authentication.getName());

        var cmd = new StartProductionOrderCommand(id, request.batchId());
        var result = startProductionOrder.execute(cmd, ActorId.of(authentication.getName()));

        if (result.isFailure()) {
            throw new ProductionOrderDomainErrorException(result.unsafeGetError());
        }

        return ResponseEntity.ok(ProductionOrderResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/complete")
    @PreAuthorize("hasAuthority('PRODUCTION_ORDER_WRITE')")
    public ResponseEntity<ProductionOrderResponse> completeProductionOrder(
            @PathVariable String id,
            Authentication authentication
    ) {
        logger.info("Completing production order: {} by actor: {}", id, authentication.getName());

        var cmd = new CompleteProductionOrderCommand(id);
        var result = completeProductionOrder.execute(cmd, ActorId.of(authentication.getName()));

        if (result.isFailure()) {
            throw new ProductionOrderDomainErrorException(result.unsafeGetError());
        }

        return ResponseEntity.ok(ProductionOrderResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/cancel")
    @PreAuthorize("hasAuthority('PRODUCTION_ORDER_WRITE')")
    public ResponseEntity<ProductionOrderResponse> cancelProductionOrder(
            @PathVariable String id,
            @Valid @RequestBody CancelProductionOrderRequest request,
            Authentication authentication
    ) {
        logger.info("Cancelling production order: {} by actor: {}, reason: {}", id, authentication.getName(), request.reason());

        var cmd = new CancelProductionOrderCommand(id, request.reason());
        var result = cancelProductionOrder.execute(cmd, ActorId.of(authentication.getName()));

        if (result.isFailure()) {
            throw new ProductionOrderDomainErrorException(result.unsafeGetError());
        }

        return ResponseEntity.ok(ProductionOrderResponse.from(result.unsafeGetValue()));
    }

    public static class ProductionOrderDomainErrorException extends RuntimeException {
        private final ProductionOrderError error;

        public ProductionOrderDomainErrorException(ProductionOrderError error) {
            super(error.message());
            this.error = error;
        }

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