package sklep.controller;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttribute;

import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import sklep.basket.Basket;
import sklep.model.Product;
import sklep.repository.ProductRepository;
import sklep.util.PhotoUtil;

@Controller
@RequestMapping("/products")
public class ProductController {
	@Autowired
	private ProductRepository productRepository;
	
	@GetMapping
	public String readAll(Model model) {
		List<Product> products = productRepository.findAll(Sort.by("productId"));
		model.addAttribute("products", products);
		return "products";
	}
	
	@GetMapping("/{numer}")
	public String readOne(Model model, @PathVariable Integer numer) {
		Optional<Product> product = productRepository.findById(numer);
		
		if(product.isPresent()) {
			model.addAttribute("product", product.get());
			return "product";
		} else {
			model.addAttribute("product_id", numer);
			return "missing_product";
		}
	}
	
	@GetMapping("/szukaj")
    public String szukaj(Model model,
                                  String name,
                                  BigDecimal min,
                                  BigDecimal max) {
        List<Product> products = List.of();
        if(name != null && !name.isEmpty() && min == null && max == null) {
            products = productRepository.findByProductNameContainsIgnoringCase(name);
        } else if ((name == null || name.isEmpty()) && (min != null || max != null)) {
            if(min == null) {
                min = BigDecimal.ZERO;
            }
            if(max == null) {
                max = BigDecimal.valueOf(1000_000_000);
            }
            products = productRepository.findByPriceBetween(min, max);
        } else if (name != null && !name.isEmpty() && (min != null || max != null)) {
            if(min == null) {
                min = BigDecimal.ZERO;
            }
            if(max == null) {
                max = BigDecimal.valueOf(1000_000_000);
            }
            products = productRepository.findByProductNameContainingIgnoringCaseAndPriceBetween(name, min, max);
        }
        model.addAttribute("products", products);
        return "wyszukiwarka2";
	}
	
	@GetMapping("/{id}/edit")
	public String editProduct(Model model, @PathVariable("id") Integer productId) {
		// W tej metodzie ładujemy dane istniejącego produktu i przechodzimy do formularza
		Optional<Product> product = productRepository.findById(productId);
		if(product.isPresent()) {
			model.addAttribute("product", product.get());
			return "product_form";
		} else {
			model.addAttribute("product_id", productId);
			return "missing_product";
		}
	}
	
    @GetMapping("/new")
    public String newProduct() {
    	// W tej metodzie wyświetlamy pusty formularz
        return "product_form";
    }
    
    @PostMapping({"/{id}/edit", "/new"})
    // Ta metoda zapisuje dane przysłane z formularza obojętnie, czy to było edit, czy new
    // Adnotacja @Valid powoduje, że Spring dokona walidacji obiektu PRZED uruchomieniem tej metody.
    // Jeśli nie ma dodatkowego parametru BindingResult, a są błędy walidacji, to Spring naszej metody w ogóle nie uruchomi.
    // Jeśli jednak metoda ma parametr BindingResult, to
    // metoda zawsze jest uruchamiana przez Springa,
    // a w tym parametrze zawarte są informacje o przebiegu walidacji, w tym błędy.
    public String saveProduct(@Valid Product product,
    		BindingResult bindingResult) {
        // W tej wersji dane z wypełnionego formularza odbieramy w postaci jednego obiektu Product.
        // Spring sam wpisze dane do pól o takich samych nazwach.
    	// Taki parametr od razu staje się częścią modelu (to jest tzw. ModelAttribute)
    	
    	if(bindingResult.hasErrors()) {
    		System.err.println("Są błedy: " + bindingResult.getAllErrors());
    		// normalnie wyświetlilibyśmy coś na stronie...
    		// ale robi to za nas tag f:form i f:errors
    		return "product_form";
    	} else {
	    	// Gdy podczas próby zapisu (operacja save) obiekt nie spełnia warunków walidacji, to jest wyrzucany wyjątek.
	        System.out.println("id przed zapisem: " + product.getProductId());
	        productRepository.save(product);
	        System.out.println("id po zapisie: " + product.getProductId());
	        return "redirect:/products";
    	}
    }
    
	@GetMapping("/{id}/add-to-basket")
	public String addToBasket(@PathVariable("id") Integer productId,
			@SessionAttribute Basket basket) {
		Optional<Product> product = productRepository.findById(productId);
		if(product.isPresent()) {
            basket.addProduct(product.get());
		} else {
			System.err.println("Nieznany produkt dodawany do koszyka: " + productId);
		}
		return "redirect:/products";
	}
	
    @GetMapping(path="/{id}/photo", produces="image/jpeg")
    @ResponseBody
    public byte[] getPhoto(@PathVariable("id") Integer productId) {
        return photoUtil.readBytes(productId);
    }
    
    @Autowired
    private PhotoUtil photoUtil;
}
