package com.nebula.erp.inventory.service;

import com.nebula.erp.inventory.model.Batch;
import com.nebula.erp.inventory.model.Inventory;
import com.nebula.erp.inventory.model.Stock;
import com.nebula.erp.inventory.repository.BatchRepository;
import com.nebula.erp.inventory.repository.InventoryRepository;
import com.nebula.erp.inventory.repository.StockRepository;
import com.nebula.erp.inventory.requestmodel.StockRequest;
import com.nebula.erp.inventory.utility.CreateLogger;
import com.nebula.erp.inventory.utility.JwtRequestUtils;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.time.Instant;

@Service
public class StockService {

    @Autowired
    private InventoryRepository inventoryRepository;

    @Autowired
    private BatchRepository batchRepository;

    @Autowired
    private StockRepository stockRepository;

    @Autowired
    private JwtRequestUtils jwtRequestUtils;

    @Autowired
    private CreateLogger createLogger;

    private static final String path = "/stock";

    // Method for adding stock
    @Transactional
    public Stock addStock(StockRequest stockRequest) {
        // *** Have to validate product is exist in master ***
        String tenantName = jwtRequestUtils.getTenantName();
        // Find inventory record for the product
        Inventory inventory = inventoryRepository.findByProductIdAndTenant(stockRequest.getProduct_id(), tenantName)
                .orElse(null);

        if (inventory == null) {
            // If no inventory exists, create a new one (First-time addition)
            inventory = new Inventory();
            inventory.setProduct_id(stockRequest.getProduct_id());
            inventory.setTotal_quantity(0); // Initialize total quantity to 0
            inventory.setTenant(tenantName);
        }

        // Update the total quantity in the inventory
        inventory.setTotal_quantity(inventory.getTotal_quantity() + stockRequest.getQuantity());
        inventory.setUpdated_at(Instant.now());
        inventoryRepository.save(inventory);

        // Handle batch (create or update existing)
        Batch batch = batchRepository.findByProductIdANDBatchCodeANDTenant(stockRequest.getProduct_id(), stockRequest.getBatch_code(), tenantName)
                .orElseGet(() -> {
                    // Create new batch if it doesn't exist
                    Batch newBatch = new Batch();
                    newBatch.setProduct_id(stockRequest.getProduct_id());
                    newBatch.setBatch_code(stockRequest.getBatch_code());
                    newBatch.setManufacture_date(stockRequest.getManufacture_date());
                    newBatch.setExpiry_date(stockRequest.getExpiry_date());
                    newBatch.setQuantity(0); // Initialize quantity to 0
                    newBatch.setTenant(tenantName);
                    newBatch.setGrn_item_id(stockRequest.getGrn_item_id());
                    return newBatch;
                });

        // Update batch quantity
        batch.setQuantity(batch.getQuantity() + stockRequest.getQuantity());
        batchRepository.save(batch);

        // Optionally log the stock
        Stock stock = new Stock();
        stock.setProduct_id(stockRequest.getProduct_id());
        stock.setBatch_id(batch.getId());
        stock.setType("IN");
        stock.setQuantity(stockRequest.getQuantity());
        stock.setReason(stockRequest.getReason());
        stock.setTenant(tenantName);
        return stockRepository.save(stock);
    }

    // Method for reducing stock
    @Transactional
    public List<Map<String, Object>> reduceStock(StockRequest stockRequest) {
        List<Map<String, Object>> stockDataList = new ArrayList<>();
        // *** Have to validate product is exist in master ***
        String stockReason = stockRequest.getReason();
        String tenantName = jwtRequestUtils.getTenantName();
        // Find inventory record for the product
        Inventory inventory = inventoryRepository.findByProductIdAndTenant(stockRequest.getProduct_id(), tenantName)
                .orElseThrow(() -> new RuntimeException("Inventory not found for product ID: " + stockRequest.getProduct_id()));

        // Check if enough stock is available
        if (inventory.getTotal_quantity() < stockRequest.getQuantity()) {
            createLogger.createLogger("error", path, "POST","Insufficient stock for product ID: " + stockRequest.getProduct_id(), "");
            throw new RuntimeException("Insufficient stock for product ID: " + stockRequest.getProduct_id());
        }

        if("SALES".equalsIgnoreCase(stockReason)) {
            // Reduce stock from the oldest batch (FIFO approach) - Sales
            List<Batch> batches = batchRepository.findByProductIdANDTenantOrderByExpiryDateAsc(stockRequest.getProduct_id(), tenantName);
            int quantityToReduce = stockRequest.getQuantity();
            for (Batch batch : batches) {
                if (quantityToReduce <= 0) break;

                if (batch.getQuantity() > 0) {
                    int quantityFromBatch = Math.min(batch.getQuantity(), quantityToReduce);

                    // Deduct from the batch
                    batch.setQuantity(batch.getQuantity() - quantityFromBatch);
                    quantityToReduce -= quantityFromBatch;

                    batchRepository.save(batch);

                    // Log the stock reduction
                    Stock stock = new Stock();
                    stock.setProduct_id(stockRequest.getProduct_id());
                    stock.setType("OUT");
                    stock.setBatch_id(batch.getId());
                    stock.setQuantity(quantityFromBatch);
                    stock.setReason(stockRequest.getReason());
                    stock.setTenant(tenantName);
                    stockRepository.save(stock);
                    Map<String, Object> stockData = new HashMap<>();
                    stockData.put("product_id", batch.getProduct_id());
                    stockData.put("quantity", quantityFromBatch);
                    stockData.put("batch_code", batch.getBatch_code());
                    stockDataList.add(stockData);
                }
            }

            if (quantityToReduce > 0) {
                createLogger.createLogger("error", path, "POST","Not enough stock available across all batches!", "");
                throw new IllegalArgumentException("Not enough stock available across all batches!");
            }
        } else if ("PURCHASE_UPDATE".equalsIgnoreCase(stockReason) ||
                "PURCHASE_DELETE".equalsIgnoreCase(stockReason) ||
                "PURCHASE_RETURN".equalsIgnoreCase(stockReason) ||
                "PURCHASE_RETURN_UPDATE".equalsIgnoreCase(stockReason) ||
                "SALES_UPDATE".equalsIgnoreCase(stockReason) ||
                "SALES_RETURN_UPDATE".equalsIgnoreCase(stockReason) ||
                "SALES_RETURN_DELETE".equalsIgnoreCase(stockReason)) {
            // Handle batch (create or update existing)
            Batch batch = batchRepository.findByProductIdANDBatchCodeANDTenant(stockRequest.getProduct_id(), stockRequest.getBatch_code(), tenantName)
                    .orElseGet(() -> {
                        // Create new batch if it doesn't exist
                        Batch newBatch = new Batch();
                        newBatch.setProduct_id(stockRequest.getProduct_id());
                        newBatch.setBatch_code(stockRequest.getBatch_code());
                        newBatch.setManufacture_date(stockRequest.getManufacture_date());
                        newBatch.setExpiry_date(stockRequest.getExpiry_date());
                        newBatch.setQuantity(0); // Initialize quantity to 0
                        newBatch.setTenant(tenantName);
                        newBatch.setGrn_item_id(stockRequest.getGrn_item_id());
                        return newBatch;
                    });

            // Check if enough stock is available
            if (batch.getQuantity() < stockRequest.getQuantity()) {
                createLogger.createLogger("error", path, "POST","Insufficient stock in batch for product ID: " + stockRequest.getProduct_id(), "");
                throw new RuntimeException("Insufficient stock in batch for product ID: " + stockRequest.getProduct_id());
            }

            // Update batch quantity
            batch.setQuantity(batch.getQuantity() - stockRequest.getQuantity());
            batchRepository.save(batch);

            // Optionally log the stock
            Stock stock = new Stock();
            stock.setProduct_id(stockRequest.getProduct_id());
            stock.setBatch_id(batch.getId());
            stock.setType("OUT");
            stock.setQuantity(stockRequest.getQuantity());
            stock.setReason(stockRequest.getReason());
            stock.setTenant(tenantName);
            stockRepository.save(stock);
        }

        // Update total quantity in inventory
        inventory.setTotal_quantity(inventory.getTotal_quantity() - stockRequest.getQuantity());
        inventory.setUpdated_at(Instant.now());
        inventoryRepository.save(inventory);
        return stockDataList;
    }
}