package com.petshop.backend.service;

import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.product.ProductRequest;
import com.petshop.backend.dto.product.ProductResponse;
import com.petshop.backend.entity.Category;
import com.petshop.backend.entity.Product;
import com.petshop.backend.exception.BusinessException;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CategoryRepository;
import com.petshop.backend.repository.ProductRepository;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Locale;

@Service
public class ProductService {

    private final ProductRepository productRepository;
    private final CategoryRepository categoryRepository;
    private final CatalogImageStorageService catalogImageStorageService;

    public ProductService(ProductRepository productRepository, CategoryRepository categoryRepository, CatalogImageStorageService catalogImageStorageService) {
        this.productRepository = productRepository;
        this.categoryRepository = categoryRepository;
        this.catalogImageStorageService = catalogImageStorageService;
    }

    public Page<ProductResponse> getAllProducts(String query, Long categoryId, Pageable pageable) {
        return productRepository.searchProducts(normalizeFilter(query), categoryId, pageable)
                .map(this::mapToResponse);
    }

    public ProductResponse getProductById(Long id) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id));
        return mapToResponse(product);
    }

    @Transactional
    public ProductResponse createProduct(ProductRequest request) {
        if (productRepository.existsByProdNameIgnoreCase(request.getProdName())) {
            throw new BusinessException("A product with this name already exists");
        }

        Category category = categoryRepository.findById(request.getCategoryId())
                .orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + request.getCategoryId()));

        Product product = new Product();
        product.setProdName(request.getProdName());
        product.setCategory(category);
        product.setProdDesc(request.getProdDesc());
        product.setProdPrice(request.getProdPrice());

        product = productRepository.save(product);
        return mapToResponse(product);
    }

    @Transactional
    public ProductResponse updateProduct(Long id, ProductRequest request) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id));

        if (productRepository.existsByProdNameIgnoreCaseAndProdIdNot(request.getProdName(), id)) {
            throw new BusinessException("A product with this name already exists");
        }

        Category category = categoryRepository.findById(request.getCategoryId())
                .orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + request.getCategoryId()));

        product.setProdName(request.getProdName());
        product.setCategory(category);
        product.setProdDesc(request.getProdDesc());
        product.setProdPrice(request.getProdPrice());

        product = productRepository.save(product);
        return mapToResponse(product);
    }

    @Transactional
    public void deleteProduct(Long id) {
        Product product = findProduct(id);
        deleteStoredImageIfPresent(product.getImageUrl());
        productRepository.delete(product);
    }

    @Transactional
    public void bulkDeleteProducts(BulkDeleteRequest request) {
        productRepository.findAllById(request.getIds()).forEach(product -> deleteStoredImageIfPresent(product.getImageUrl()));
        productRepository.deleteAllById(request.getIds());
    }

    @Transactional
    public ProductResponse uploadProductImage(Long id, MultipartFile file) throws IOException {
        validateImageFile(file);
        Product product = findProduct(id);
        deleteStoredImageIfPresent(product.getImageUrl());
        product.setImageUrl(catalogImageStorageService.storeProductImage(file));
        return mapToResponse(productRepository.save(product));
    }

    @Transactional
    public ProductResponse deleteProductImage(Long id) {
        Product product = findProduct(id);
        deleteStoredImageIfPresent(product.getImageUrl());
        product.setImageUrl(null);
        return mapToResponse(productRepository.save(product));
    }

    public ImagePayload loadProductImage(Long id) {
        Product product = findProduct(id);
        if (product.getImageUrl() == null || product.getImageUrl().isBlank()) {
            throw new ResourceNotFoundException("Product image not found with id: " + id);
        }
        Resource resource = catalogImageStorageService.loadProductImage(product.getImageUrl());
        MediaType mediaType = catalogImageStorageService.resolveMediaType(resource);
        return new ImagePayload(resource, mediaType);
    }

    private Product findProduct(Long id) {
        return productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id));
    }

    private void validateImageFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("Please select an image to upload");
        }
        if (file.getSize() > 5 * 1024 * 1024) {
            throw new IllegalArgumentException("Image file size must be less than 5MB");
        }
        String contentType = file.getContentType();
        if (contentType == null) {
            throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
        }
        String normalized = contentType.toLowerCase(Locale.ROOT);
        if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/gif")) {
            throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
        }
    }

    private void deleteStoredImageIfPresent(String storedImagePath) {
        if (storedImagePath == null || storedImagePath.isBlank()) {
            return;
        }
        try {
            catalogImageStorageService.deleteProductImage(storedImagePath);
        } catch (IOException | IllegalArgumentException ignored) {
        }
    }

    private ProductResponse mapToResponse(Product product) {
        return new ProductResponse(
            product.getProdId(),
            product.getProdName(),
            product.getCategory().getCategoryId(),
            product.getCategory().getCategoryName(),
            product.getProdDesc(),
            product.getProdPrice(),
            product.getImageUrl() != null && !product.getImageUrl().isBlank() ? "/api/v1/products/" + product.getProdId() + "/image" : null,
            product.getCreatedAt(),
            product.getUpdatedAt()
        );
    }

    public record ImagePayload(Resource resource, MediaType mediaType) {
    }

    private String normalizeFilter(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
}
