package com.nebula.erp.product.service;

import com.nebula.erp.product.model.*;
import com.nebula.erp.product.repository.*;
import com.nebula.erp.product.requestmodel.ProductRequest;
import com.nebula.erp.product.utility.CreateLogger;
import com.nebula.erp.product.utility.JwtRequestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

@Service
public class ProductService {

    @Autowired
    private S3Service s3Service;

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private ImageRepository imageRepository;

    @Autowired
    private BrandRepository brandRepository;

    @Autowired
    private TaxRepository taxRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private JwtRequestUtils jwtRequestUtils;

    @Autowired
    private CreateLogger createLogger;

    private static final String path = "/products";

    @Value("${image.upload.dir}")
    private String imageUploadDir;

    @Value("${image.storage.location}")
    private String imageStorageLocation;

    public Page<Product> getAllProducts(int page, int size, String name, Integer productId, Integer brandId,
            Integer categoryId,String search) {
        String tenantName = jwtRequestUtils.getTenantName();

        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "id"));

        // Use a specification or dynamic query to apply filters
        Specification<Product> spec = Specification.where(null);

        // Filter by tenant name
        if (!"ALL".equals(tenantName)) {
            spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("tenant"), tenantName));
        }

        // Filter by name if provided
        if (name != null && !name.isEmpty()) {
            spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                    .like(criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"));
        }

        // Filter by product ID if provided
        if (productId != null) {
            spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("id"), productId));
        }

        // Filter by brand ID if provided
        if (brandId != null) {
            spec = spec
                    .and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("brand").get("id"), brandId));
        }

        // Filter by category ID if provided
        if (categoryId != null) {
            spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("category").get("id"),
                    categoryId));
        }
        // SEARCH (code, name, brand, category)
        if (search != null && !search.trim().isEmpty()) {
            return productRepository.searchProducts(
                    tenantName,
                    "%" + search.toLowerCase() + "%",
                    pageable);
        }

        // Add more filter conditions as required
        return productRepository.findAll(spec, pageable);
    }

    // Get sales count
    public Integer getProductsCount(String tenant_name) {
        return productRepository.findCountByTenant(tenant_name);
    }

    public Product createProductWithImages(ProductRequest productRequest, List<MultipartFile> productImages)
            throws IOException {
        // Validate required fields
        if (productRequest.getName() == null || productRequest.getName().isEmpty()) {
            createLogger.createLogger("error", path, "POST", "Product name is required", "");
            throw new IllegalArgumentException("Product name is required");
        }

        String tenantName = jwtRequestUtils.getTenantName();
        String userId = jwtRequestUtils.getUserId();

        // Create a new Product object
        Product product = new Product();
        product.setCode(productRequest.getCode());
        product.setName(productRequest.getName());
        product.setDescription(productRequest.getDescription());
        product.setForm(productRequest.getForm());
        product.setDosage(productRequest.getDosage());
        product.setUnit_of_measurement(productRequest.getUnit_of_measurement());
        product.setQuantity(productRequest.getQuantity());
        product.setPurchase_price(productRequest.getPurchase_price());
        product.setSelling_price(productRequest.getSelling_price());
        product.setMrp(productRequest.getMrp());
        product.setMin_stock(productRequest.getMin_stock());
        product.setHsn_code(productRequest.getHsn_code());
        product.setRemarks(productRequest.getRemarks());
        product.setCreated_by(userId);
        product.setTenant(tenantName);

        // Set brand and category if provided
        if (productRequest.getBrand_id() != null) {
            Brand brand = brandRepository.findById(productRequest.getBrand_id())
                    .orElseThrow(() -> new RuntimeException("Brand not found"));
            product.setBrand(brand);
        }

        if (productRequest.getCategory_id() != null) {
            Category category = categoryRepository.findById(productRequest.getCategory_id())
                    .orElseThrow(() -> new RuntimeException("Category not found"));
            product.setCategory(category);
        }

        // Set tax if provided
        if (productRequest.getTax_id() != null) {
            Tax tax = taxRepository.findById(productRequest.getTax_id())
                    .orElseThrow(() -> new RuntimeException("Tax not found"));
            product.setTax(tax);
        }

        // Save the product along with the images
        Product savedProduct = productRepository.save(product);
        if (productImages != null) {
            saveImages(savedProduct, productImages, tenantName);
        }
        return savedProduct;
    }

    public Optional<Product> getProduct(Long id) {
        String tenantName = jwtRequestUtils.getTenantName();
        return productRepository.findByIdAndTenant(id, tenantName);
    }

    public Product updateProduct(Long id, ProductRequest productRequest, List<MultipartFile> productImages)
            throws IOException {
        String tenantName = jwtRequestUtils.getTenantName();

        // Fetch the existing product from the database
        Product product = productRepository.findByIdAndTenant(id, tenantName)
                .orElseThrow(() -> new RuntimeException("Medication not found"));

        // Update product basic fields
        product.setCode(productRequest.getCode());
        product.setName(productRequest.getName());
        product.setDescription(productRequest.getDescription());
        product.setForm(productRequest.getForm());
        product.setDosage(productRequest.getDosage());
        product.setUnit_of_measurement(productRequest.getUnit_of_measurement());
        product.setQuantity(productRequest.getQuantity());
        product.setPurchase_price(productRequest.getPurchase_price());
        product.setSelling_price(productRequest.getSelling_price());
        product.setMrp(productRequest.getMrp());
        product.setMin_stock(productRequest.getMin_stock());
        product.setHsn_code(productRequest.getHsn_code());
        product.setRemarks(productRequest.getRemarks());

        // Update the relationship with Brand if provided
        if (productRequest.getBrand_id() != null) {
            Brand brand = brandRepository.findById(productRequest.getBrand_id())
                    .orElseThrow(() -> new RuntimeException("Brand not found"));
            product.setBrand(brand);
        }

        // Update the relationship with Category if provided
        if (productRequest.getCategory_id() != null) {
            Category category = categoryRepository.findById(productRequest.getCategory_id())
                    .orElseThrow(() -> new RuntimeException("Category not found"));
            product.setCategory(category);
        }

        // Update the relationship with Tax if provided
        if (productRequest.getTax_id() != null) {
            Tax tax = taxRepository.findById(productRequest.getTax_id())
                    .orElseThrow(() -> new RuntimeException("Tax not found"));
            product.setTax(tax);
        }

        // Handle updating Images
        if (productImages != null && !productImages.isEmpty()) {
            saveImages(product, productImages, tenantName);
        }

        // Save the updated product with all the related entities
        return productRepository.save(product);
    }

    // Helper function for save image
    private void saveImages(Product product, List<MultipartFile> images, String tenantName) {
        List<String> fileUrls = new ArrayList<>();
        if ("local".equalsIgnoreCase(imageStorageLocation)) {
            fileUrls = imageUpload(images);
        } else if ("aws".equalsIgnoreCase(imageStorageLocation)) {
            fileUrls = s3Service.uploadMultipleFiles(images, tenantName);
        } else {
            fileUrls = imageUpload(images);
        }

        fileUrls.forEach(url -> {
            try {
                Image image = new Image();
                image.setProduct(product);
                image.setImage(url);
                image.setCreated_by(product.getCreated_by());
                image.setTenant(product.getTenant());
                imageRepository.save(image);
            } catch (Exception e) {
                createLogger.createLogger("error", path, "POST", e.getMessage(), "");
            }
        });
    }

    // Upload image in local environment
    private List<String> imageUpload(List<MultipartFile> images) {
        List<String> fileUrls = new ArrayList<>();
        for (MultipartFile file : images) {
            try {
                String contentType = file.getContentType();
                if (!isValidImage(contentType)) {
                    createLogger.createLogger("error", path, "POST",
                            "Invalid image format. Only JPEG, PNG are allowed.", "");
                    throw new IllegalArgumentException("Invalid image format. Only JPEG, PNG are allowed.");
                }
                // Get the original filename
                String originalFilename = file.getOriginalFilename();
                // Generate a unique filename
                String uniqueFilename = UUID.randomUUID().toString() + "_" + originalFilename;
                // Define the file path to save the image
                Path imagePath = Paths.get(imageUploadDir, uniqueFilename);

                // Ensure directory exists
                Files.createDirectories(imagePath.getParent());

                // Write the file to the file system
                Files.write(imagePath, file.getBytes());
                fileUrls.add(imagePath.toString());
            } catch (Exception e) {
                createLogger.createLogger("error", path, "POST", e.getMessage(), "");
            }
        }
        return fileUrls;
    }

    // Helper method to validate image format
    private boolean isValidImage(String contentType) {
        return contentType != null && (contentType.equals("image/jpeg") || contentType.equals("image/png"));
    }

}