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

import de.effigenix.application.masterdata.article.*;
import de.effigenix.application.masterdata.article.command.*;
import de.effigenix.application.masterdata.article.AssignSupplier;
import de.effigenix.application.masterdata.supplier.RemoveSupplier;
import de.effigenix.application.masterdata.article.command.AssignSupplierCommand;
import de.effigenix.application.masterdata.supplier.command.RemoveSupplierCommand;
import de.effigenix.domain.masterdata.article.Article;
import de.effigenix.domain.masterdata.article.ArticleError;
import de.effigenix.domain.masterdata.article.ArticleId;
import de.effigenix.domain.masterdata.article.ArticleStatus;
import de.effigenix.domain.masterdata.productcategory.ProductCategoryId;
import de.effigenix.infrastructure.masterdata.web.dto.*;
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/articles")
@SecurityRequirement(name = "Bearer Authentication")
@Tag(name = "Articles", description = "Article management endpoints")
public class ArticleController {

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

    private final CreateArticle createArticle;
    private final UpdateArticle updateArticle;
    private final GetArticle getArticle;
    private final ListArticles listArticles;
    private final ActivateArticle activateArticle;
    private final DeactivateArticle deactivateArticle;
    private final AddSalesUnit addSalesUnit;
    private final RemoveSalesUnit removeSalesUnit;
    private final UpdateSalesUnitPrice updateSalesUnitPrice;
    private final AssignSupplier assignSupplier;
    private final RemoveSupplier removeSupplier;

    public ArticleController(
            CreateArticle createArticle,
            UpdateArticle updateArticle,
            GetArticle getArticle,
            ListArticles listArticles,
            ActivateArticle activateArticle,
            DeactivateArticle deactivateArticle,
            AddSalesUnit addSalesUnit,
            RemoveSalesUnit removeSalesUnit,
            UpdateSalesUnitPrice updateSalesUnitPrice,
            AssignSupplier assignSupplier,
            RemoveSupplier removeSupplier
    ) {
        this.createArticle = createArticle;
        this.updateArticle = updateArticle;
        this.getArticle = getArticle;
        this.listArticles = listArticles;
        this.activateArticle = activateArticle;
        this.deactivateArticle = deactivateArticle;
        this.addSalesUnit = addSalesUnit;
        this.removeSalesUnit = removeSalesUnit;
        this.updateSalesUnitPrice = updateSalesUnitPrice;
        this.assignSupplier = assignSupplier;
        this.removeSupplier = removeSupplier;
    }

    @PostMapping
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> createArticle(
            @Valid @RequestBody CreateArticleRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Creating article: {} by actor: {}", request.articleNumber(), actorId.value());

        var cmd = new CreateArticleCommand(
                request.name(), request.articleNumber(), request.categoryId(),
                request.unit(), request.priceModel(), request.price()
        );
        var result = createArticle.execute(cmd, actorId);

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

        logger.info("Article created: {}", request.articleNumber());
        return ResponseEntity.status(HttpStatus.CREATED).body(ArticleResponse.from(result.unsafeGetValue()));
    }

    @GetMapping
    public ResponseEntity<List<ArticleResponse>> listArticles(
            @RequestParam(value = "categoryId", required = false) String categoryId,
            @RequestParam(value = "status", required = false) ArticleStatus status,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Listing articles by actor: {}", actorId.value());

        Result<ArticleError, List<Article>> result;
        if (categoryId != null) {
            result = listArticles.executeByCategory(ProductCategoryId.of(categoryId));
        } else if (status != null) {
            result = listArticles.executeByStatus(status);
        } else {
            result = listArticles.execute();
        }

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

        var response = result.unsafeGetValue().stream().map(ArticleResponse::from).toList();
        return ResponseEntity.ok(response);
    }

    @GetMapping("/{id}")
    public ResponseEntity<ArticleResponse> getArticle(
            @PathVariable("id") String articleId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Getting article: {} by actor: {}", articleId, actorId.value());

        var result = getArticle.execute(ArticleId.of(articleId));

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

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

    @PutMapping("/{id}")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> updateArticle(
            @PathVariable("id") String articleId,
            @Valid @RequestBody UpdateArticleRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Updating article: {} by actor: {}", articleId, actorId.value());

        var cmd = new UpdateArticleCommand(articleId, request.name(), request.categoryId());
        var result = updateArticle.execute(cmd, actorId);

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

        logger.info("Article updated: {}", articleId);
        return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/activate")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> activate(
            @PathVariable("id") String articleId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Activating article: {} by actor: {}", articleId, actorId.value());

        var result = activateArticle.execute(ArticleId.of(articleId), actorId);

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

        logger.info("Article activated: {}", articleId);
        return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/deactivate")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> deactivate(
            @PathVariable("id") String articleId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Deactivating article: {} by actor: {}", articleId, actorId.value());

        var result = deactivateArticle.execute(ArticleId.of(articleId), actorId);

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

        logger.info("Article deactivated: {}", articleId);
        return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/sales-units")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> addSalesUnit(
            @PathVariable("id") String articleId,
            @Valid @RequestBody AddSalesUnitRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Adding sales unit to article: {} by actor: {}", articleId, actorId.value());

        var cmd = new AddSalesUnitCommand(articleId, request.unit(), request.priceModel(), request.price());
        var result = addSalesUnit.execute(cmd, actorId);

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

        logger.info("Sales unit added to article: {}", articleId);
        return ResponseEntity.status(HttpStatus.CREATED).body(ArticleResponse.from(result.unsafeGetValue()));
    }

    @DeleteMapping("/{id}/sales-units/{suId}")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<Void> removeSalesUnit(
            @PathVariable("id") String articleId,
            @PathVariable("suId") String salesUnitId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Removing sales unit {} from article: {} by actor: {}", salesUnitId, articleId, actorId.value());

        var cmd = new RemoveSalesUnitCommand(articleId, salesUnitId);
        var result = removeSalesUnit.execute(cmd, actorId);

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

        logger.info("Sales unit removed from article: {}", articleId);
        return ResponseEntity.noContent().build();
    }

    @PutMapping("/{id}/sales-units/{suId}/price")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> updateSalesUnitPrice(
            @PathVariable("id") String articleId,
            @PathVariable("suId") String salesUnitId,
            @Valid @RequestBody UpdateSalesUnitPriceRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Updating price of sales unit {} in article: {} by actor: {}", salesUnitId, articleId, actorId.value());

        var cmd = new UpdateSalesUnitPriceCommand(articleId, salesUnitId, request.price());
        var result = updateSalesUnitPrice.execute(cmd, actorId);

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

        logger.info("Sales unit price updated in article: {}", articleId);
        return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
    }

    @PostMapping("/{id}/suppliers")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<ArticleResponse> assignSupplier(
            @PathVariable("id") String articleId,
            @Valid @RequestBody AssignSupplierRequest request,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Assigning supplier {} to article: {} by actor: {}", request.supplierId(), articleId, actorId.value());

        var cmd = new AssignSupplierCommand(articleId, request.supplierId());
        var result = assignSupplier.execute(cmd, actorId);

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

        logger.info("Supplier assigned to article: {}", articleId);
        return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
    }

    @DeleteMapping("/{id}/suppliers/{supplierId}")
    @PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
    public ResponseEntity<Void> removeSupplier(
            @PathVariable("id") String articleId,
            @PathVariable("supplierId") String supplierId,
            Authentication authentication
    ) {
        var actorId = extractActorId(authentication);
        logger.info("Removing supplier {} from article: {} by actor: {}", supplierId, articleId, actorId.value());

        var cmd = new RemoveSupplierCommand(articleId, supplierId);
        var result = removeSupplier.execute(cmd, actorId);

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

        logger.info("Supplier removed from article: {}", articleId);
        return ResponseEntity.noContent().build();
    }

    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 ArticleDomainErrorException extends RuntimeException {
        private final ArticleError error;

        public ArticleDomainErrorException(ArticleError error) {
            super(error.message());
            this.error = error;
        }

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