package api

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"

	"github.com/gorilla/mux"
	"gorm.io/gorm"
)

type Product struct {
	gorm.Model `json:"-"`
	Code       int    `json:"code" gorm:"unique;index"`
	Name       string `json:"name" gorm:"unique;index"`
	Price      int    `json:"price"`
	Stock      int    `json:"stock"`
}

func (a *api) AddProduct(w http.ResponseWriter, req *http.Request) {
	var product Product
	err := json.NewDecoder(req.Body).Decode(&product)
	if err != nil {
		log.Printf("Can't create product: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	err = a.db.Create(&product).Error
	if err != nil {
		log.Printf("Can't create product: %v\n%v", err, product)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	err = json.NewEncoder(w).Encode(product)
	if err != nil {
		log.Printf("Can't encode added product: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (a *api) ListProducts(w http.ResponseWriter, req *http.Request) {
	var products []Product
	err := a.db.Find(&products).Error
	if err != nil {
		log.Printf("Can't list products: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(products)
	if err != nil {
		log.Printf("Can't encode products: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (a *api) GetProduct(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	var product Product
	err := a.db.Where("code = ?", vars["code"]).First(&product).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		log.Printf("Can't get product %s: %v", vars["code"], err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(product)
	if err != nil {
		log.Printf("Can't encode product: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (a *api) DeleteProduct(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	err := a.db.Where("code = ?", vars["code"]).Delete(&Product{}).Error
	if err != nil {
		log.Printf("Can't delete product %s: %v", vars["code"], err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (a *api) UpdateProduct(w http.ResponseWriter, req *http.Request) {
	var product Product
	err := json.NewDecoder(req.Body).Decode(&product)
	if err != nil {
		log.Printf("Can't decode memeber: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	vars := mux.Vars(req)
	var dbProduct Product
	err = a.db.Where("code = ?", vars["code"]).First(&dbProduct).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		log.Printf("Can't get product %s: %v", vars["code"], err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if product.Code != 0 {
		dbProduct.Code = product.Code
	}
	if product.Name != "" {
		dbProduct.Name = product.Name
	}
	if product.Price >= 0 {
		dbProduct.Price = product.Price
	}
	if product.Stock >= 0 {
		dbProduct.Stock = product.Stock
	}
	err = a.db.Save(&dbProduct).Error
	if err != nil {
		log.Printf("Can't update product %s: %v %v", vars["code"], err, product)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusAccepted)
	err = json.NewEncoder(w).Encode(dbProduct)
	if err != nil {
		log.Printf("Can't encode updated product: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}