package controllers

import (
	"nearle/db"
	"nearle/domain"
	"nearle/models"
	"nearle/utils"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
)

func GetProductInfo(c *fiber.Ctx) error {

	pid, _ := strconv.Atoi(c.Query("productid"))

	utils.Logger.Debugf("productid: %d", pid)

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
	})

}

func GetProducts(c *fiber.Ctx) error {

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
	})

}

func GetProductCategory(c *fiber.Ctx) error {
	data := domain.GetProductCategory()

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": data,
	})
}

func GetProductSubCategory(c *fiber.Ctx) error {
	categoryID, _ := strconv.Atoi(c.Query("categoryid"))
	tenantID, _ := strconv.Atoi(c.Query("tenantid"))

	data := domain.GetProductSubCategory(categoryID, tenantID)

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": data,
	})
}

func CreateProductVariant(c *fiber.Ctx) error {
	var input models.Productvariant

	if err := c.BodyParser(&input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{
			"code":    http.StatusBadRequest,
			"message": "Invalid request body",
			"status":  false,
		})
	}

	if err := domain.CreateProductVariant(input); err != nil {
		return c.JSON(fiber.Map{
			"code":    http.StatusConflict,
			"message": "Failed",
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
	})
}

func CreateProduct(c *fiber.Ctx) error {
	var product models.Products

	if err := c.BodyParser(&product); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{
			"code":    http.StatusBadRequest,
			"message": "Invalid request body",
			"status":  false,
		})
	}

	if err := domain.CreateProduct(&product); err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": "Failed to create product",
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Product created successfully",
		"status":  true,
	})
}

func GetAllProducts(c *fiber.Ctx) error {
	categoryID, _ := strconv.Atoi(c.Query("categoryid"))
	subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid"))
	productID, _ := strconv.Atoi(c.Query("productid"))
	applocationID, _ := strconv.Atoi(c.Query("applocationid"))
	tenantID, _ := strconv.Atoi(c.Query("tenantid"))
	locationID, _ := strconv.Atoi(c.Query("locationid"))
	keyword := c.Query("keyword", "")
	productStatus := c.Query("productstatus", "")
	approve := c.Query("approve", "")
	pageno, _ := strconv.Atoi(c.Query("pageno"))
	pagesize, _ := strconv.Atoi(c.Query("pagesize"))

	details, err := domain.FetchFilteredProducts(
		categoryID, subcategoryID, productID, applocationID, tenantID,
		locationID, keyword, productStatus, approve, pageno, pagesize,
	)
	if err != nil {
		return c.JSON(fiber.Map{
			"code":    http.StatusConflict,
			"message": "Failed",
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"data":    details,
	})
}

func UpdateProduct(c *fiber.Ctx) error {

	var data models.Products

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	err := domain.UpdateProduct(data)

	if err != nil {
		return c.JSON(fiber.Map{
			"status":  false,
			"code":    http.StatusConflict,
			"message": err,
		})

	}

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusAccepted,
		"message": "Product update successful",
	})

}

func DeleteProduct(c *fiber.Ctx) error {
	// Get product ID from query
	pidStr := c.Query("productid")
	pid, err := strconv.Atoi(pidStr)
	if err != nil || pid <= 0 {
		return c.JSON(fiber.Map{
			"code":    http.StatusBadRequest,
			"message": "Invalid product ID",
			"status":  false,
		})
	}

	// Start transaction
	tx := db.DB.Begin()
	if tx.Error != nil {
		return c.JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": "Failed to start transaction",
			"status":  false,
		})
	}

	// Delete product
	if err := tx.Table("products").Where("productid = ?", pid).Delete(&models.Products{}).Error; err != nil {
		tx.Rollback()
		return c.JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": "Error deleting product",
			"status":  false,
		})
	}

	// Commit transaction
	if err := tx.Commit().Error; err != nil {
		return c.JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": "Failed to commit transaction",
			"status":  false,
		})
	}

	// Success response
	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
	})
}

func GetProductsBySubcategory(c *fiber.Ctx) error {
	// log.Println("Handler: GetProductsBySubcategory called")

	// Required query param: categoryid
	categoryIDStr := c.Query("categoryid")
	if categoryIDStr == "" {
		utils.Error("Error: categoryid is missing")
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    400,
			"status":  false,
			"message": "categoryid is required",
			"data":    fiber.Map{},
		})
	}

	categoryID, err := strconv.Atoi(categoryIDStr)
	if err != nil {
		utils.Logger.Errorf("Error: Invalid categoryid: %s", categoryIDStr)
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    400,
			"status":  false,
			"message": "Invalid categoryid",
			"data":    fiber.Map{},
		})
	}

	// Optional tenantid
	tenantIDStr := c.Query("tenantid")
	tenantID := 0
	if tenantIDStr != "" {
		tenantID, err = strconv.Atoi(tenantIDStr)
		if err != nil {
			utils.Logger.Errorf("Error: Invalid tenantid: %s", tenantIDStr)
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"code":    400,
				"status":  false,
				"message": "Invalid tenantid",
				"data":    fiber.Map{},
			})
		}
	}

	// Optional query params
	applocationIDStr := c.Query("applocationid", "0")
	applocationID, _ := strconv.Atoi(applocationIDStr)

	productIDStr := c.Query("productid")
	keyword := c.Query("keyword")

	locationIDStr := c.Query("locationid")
	locationID := 0
	if locationIDStr != "" {
		locationID, _ = strconv.Atoi(locationIDStr)
	}

	productID := 0
	if productIDStr != "" {
		productID, err = strconv.Atoi(productIDStr)
		if err != nil {
			utils.Logger.Errorf("Error: Invalid productid: %s", productIDStr)
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"code":    400,
				"status":  false,
				"message": "Invalid productid",
				"data":    fiber.Map{},
			})
		}
	}

	// Fetch subcategories
	var subcategories []models.Subcategory
	utils.Logger.Infof("Fetching subcategories for categoryid: %d", categoryID)
	if err := db.DB.Table("productsubcategories").
		Where("categoryid = ?", categoryID).
		Find(&subcategories).Error; err != nil {
		utils.Logger.Errorf("Error fetching subcategories: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"status":  false,
			"message": "Error fetching subcategories",
			"data":    fiber.Map{},
		})
	}

	// Build product query
	var allProducts []models.Products
	query := db.DB.
		Table("products a").
		Joins("LEFT JOIN productlocations pl ON pl.productid = a.productid").
		Where("a.categoryid = ?", categoryID)

	if tenantID > 0 {
		query = query.Where("a.tenantid = ?", tenantID)
	}

	if locationID > 0 {
		utils.Logger.Infof("Filtering by locationid: %d", locationID)
		query = query.Where("pl.locationid = ?", locationID)
	} else {
		utils.Info("No locationid provided or value is 0 — showing all locations")
	}

	if applocationID > 0 {
		query = query.Where("a.applocationid = ?", applocationID)
	}

	if productID > 0 {
		query = query.Where("a.productid = ?", productID)
	}

	if keyword != "" {
		utils.Info("Applying keyword filter")
		like := "%" + strings.ToLower(keyword) + "%"
		query = query.Where(
			db.DB.Where("LOWER(a.productname) LIKE ?", like).
				Or("LOWER(a.unitvalue) LIKE ?", like).
				Or("LOWER(CAST(a.productcost AS CHAR)) LIKE ?", like),
		)
	}

	if err := query.Select("a.*").Find(&allProducts).Error; err != nil {
		utils.Logger.Errorf("Error fetching products: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"status":  false,
			"message": "Error fetching products",
			"data":    fiber.Map{},
		})
	}

	// Group products under subcategories
	var details []models.SubcategoryProductResponse
	var uncategorizedProducts []models.Products

	for _, subcat := range subcategories {
		var subcatProducts []models.Products
		for _, prod := range allProducts {
			if prod.Subcategoryid != nil && *prod.Subcategoryid == subcat.Subcategoryid {
				subcatProducts = append(subcatProducts, prod)
			}
		}
		if len(subcatProducts) > 0 {
			details = append(details, models.SubcategoryProductResponse{
				SubcategoryID:   subcat.Subcategoryid,
				SubcategoryName: subcat.Subcategoryname,
				Products:        subcatProducts,
			})
		}
	}

	for _, prod := range allProducts {
		if prod.Subcategoryid == nil {
			uncategorizedProducts = append(uncategorizedProducts, prod)
		}
	}
	if len(uncategorizedProducts) > 0 {
		details = append(details, models.SubcategoryProductResponse{
			SubcategoryID:   0,
			SubcategoryName: "Uncategorized",
			Products:        uncategorizedProducts,
		})
	}

	// Tenant info block only if tenantID is passed
	if tenantID > 0 {
		var tenantInfo = fiber.Map{}
		var tenantFound bool
		for _, p := range allProducts {
			if p.Tenantid != nil {
				var tenant struct {
					Tenantname       string
					Address          string
					Licenseno        string
					Primaryemail     string
					Primarycontact   string
					Locationname     string
					Pickuplocationid int
					Suburb           string
					City             string
					Latitude         string
					Longitude        string
					Postcode         string
				}

				err := db.DB.Raw(`
					SELECT 
						t.tenantname,
						t.address,
						t.licenseno,
						t.primaryemail,
						t.primarycontact,
						l.locationid AS pickuplocationid,
						l.suburb,
						l.city,
						l.latitude,
						l.longitude,
						l.postcode,
						a.locationname
					FROM tenants t
					LEFT JOIN tenantlocations l ON t.tenantid = l.tenantid
					LEFT JOIN app_location a ON l.applocationid = a.applocationid
					WHERE t.tenantid = ? AND t.applocationid = ?
					LIMIT 1
				`, *p.Tenantid, applocationID).Scan(&tenant).Error

				if err == nil {
					tenantInfo = fiber.Map{
						"tenantname":       tenant.Tenantname,
						"address":          tenant.Address,
						"licenseno":        tenant.Licenseno,
						"primaryemail":     tenant.Primaryemail,
						"primarycontact":   tenant.Primarycontact,
						"locationname":     tenant.Locationname,
						"pickuplocationid": tenant.Pickuplocationid,
						"suburb":           tenant.Suburb,
						"city":             tenant.City,
						"pickuplat":        tenant.Latitude,
						"pickuplong":       tenant.Longitude,
						"postcode":         tenant.Postcode,
						"details":          details,
					}
					tenantFound = true
				}
				break
			}
		}

		if tenantFound {
			return c.Status(fiber.StatusOK).JSON(fiber.Map{
				"code":    200,
				"status":  true,
				"message": "Success",
				"data":    tenantInfo,
			})
		}
	}

	// Return only details if tenantid is not provided
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"code":    200,
		"status":  true,
		"message": "Success",
		"data": fiber.Map{
			"details": details,
		},
	})
}

func GetProductCount(c *fiber.Ctx) error {
	tenantID, _ := strconv.Atoi(c.Query("tenantid"))
	categoryID, _ := strconv.Atoi(c.Query("categoryid"))
	subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid"))
	approve := c.Query("approve", "") // New line

	result, err := domain.Getproductcount(tenantID, categoryID, subcategoryID, approve)
	if err != nil {
		return c.JSON(fiber.Map{
			"code":    http.StatusConflict,
			"message": "failed",
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": result,
	})
}

func GetProductByVariant(c *fiber.Ctx) error {

	tenantID, _ := strconv.Atoi(c.Query("tenantid"))
	variantid, _ := strconv.Atoi(c.Query("variantid"))

	result, err := domain.GetproductbyVariant(tenantID, variantid)

	if err != nil {

		return c.JSON(fiber.Map{
			"code":    http.StatusConflict,
			"message": "failed",
			"status":  false,
		})

	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": result,
	})

}

func GetCatalougeProducts(c *fiber.Ctx) error {
	tenantId, _ := strconv.Atoi(c.Query("tenantid"))
	locationid, _ := strconv.Atoi(c.Query("locationid"))
	subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
	keyword := c.Query("keyword")
	pageno, _ := strconv.Atoi(c.Query("pageno"))
	pagesize, _ := strconv.Atoi(c.Query("pagesize"))

	data := domain.GetCatalougeProducts(tenantId, locationid, subcategoryid, pageno, pagesize, keyword)

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": data,
	})

}

func GetStockstatement(c *fiber.Ctx) error {
	tenantId, _ := strconv.Atoi(c.Query("tenantid"))
	locationid, _ := strconv.Atoi(c.Query("locationid"))
	subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
	pageno, _ := strconv.Atoi(c.Query("pageno"))
	pagesize, _ := strconv.Atoi(c.Query("pagesize"))
	keyword := c.Query("keyword")

	data := domain.GetStockStatement(tenantId, locationid, subcategoryid, pageno, pagesize, keyword)

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": data,
	})

}

func GetProductVariants(c *fiber.Ctx) error {
	tenantId, _ := strconv.Atoi(c.Query("tenantid"))
	subcategoryId, _ := strconv.Atoi(c.Query("subcategoryid"))

	data := domain.GetProductVariants(tenantId, subcategoryId)

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": data,
	})
}

func CreateProductLocation(c *fiber.Ctx) error {
	var data []models.Productlocations

	if err := c.BodyParser(&data); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    400,
			"message": "Invalid request body",
			"status":  false,
		})
	}
	err := domain.CreateProductLocation(data)

	if err != nil {
		return c.JSON(fiber.Map{
			"status":  false,
			"code":    http.StatusConflict,
			"message": err,
		})

	}

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusCreated,
		"message": "Success",
	})

}

func UpdateProductLocation(c *fiber.Ctx) error {

	var data models.Productlocations

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	err := domain.UpdateProductLocation(data)

	if err != nil {
		return c.JSON(fiber.Map{
			"status":  false,
			"code":    http.StatusConflict,
			"message": err,
		})

	}

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusAccepted,
		"message": "Product update successful",
	})

}

func CreateProductStock(c *fiber.Ctx) error {
	var stocks []models.Productstock

	if err := c.BodyParser(&stocks); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    400,
			"message": "Invalid request body",
			"status":  false,
		})
	}

	for i := range stocks {

		stocks[i].Stockdate = time.Now()

	}

	// Insert all stock records
	if err := db.DB.Table("productstocks").Create(&stocks).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"message": "Failed to create product stocks",
			"status":  false,
		})
	}

	// Collect and dedupe product IDs to update their status
	idMap := make(map[int]struct{})
	var productIDs []int
	for _, s := range stocks {
		if s.Productid <= 0 {
			continue
		}
		if _, ok := idMap[s.Productid]; !ok {
			idMap[s.Productid] = struct{}{}
			productIDs = append(productIDs, s.Productid)
		}
	}

	// Only update if we have product ids
	if len(productIDs) > 0 {
		if err := db.DB.Table("products").
			Where("productid IN ?", productIDs).
			Update("productstatus", "available").Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"code":    500,
				"message": "Failed to update product status",
				"status":  false,
			})
		}
	}

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"code":    201,
		"message": "Product stocks created successfully",
		"status":  true,
		"details": stocks,
	})
}

func UpdateProductStock(c *fiber.Ctx) error {
	var stock models.Productstock

	// Parse body
	if err := c.BodyParser(&stock); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    400,
			"message": "Invalid request body",
			"status":  false,
		})
	}

	// Validate required ID
	if stock.Productstockid == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    400,
			"message": "Missing productstockid",
			"status":  false,
		})
	}

	// Attempt update
	if err := domain.UpdateProductStock(&stock); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"message": "Failed to update product stock",
			"status":  false,
		})
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"code":    200,
		"message": "Product stock updated successfully",
		"status":  true,
	})
}

func GetProductStocks(c *fiber.Ctx) error {
	tenantID := c.Query("tenantid")
	locationID := c.Query("locationid")

	stocks, err := domain.GetProductStocks(tenantID, locationID)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"message": "Failed to fetch product stocks",
			"status":  false,
		})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{
		"code":    200,
		"message": "Product stocks fetched successfully",
		"status":  true,
		"data":    stocks,
	})
}

func GetLocationProducts(c *fiber.Ctx) error {

	tenantid, _ := strconv.Atoi(c.Query("tenantid"))
	locationid, _ := strconv.Atoi(c.Query("locationid"))
	subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
	keyword := c.Query("keyword")
	pageno, _ := strconv.Atoi(c.Query("pageno"))
	pagesize, _ := strconv.Atoi(c.Query("pagesize"))

	result := domain.GetLocationProducts(tenantid, locationid, subcategoryid, pageno, pagesize, keyword)

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusOK,
		"message": "Success",
		"details": result,
	})
}

func GetSubCategoryWiseSummary(c *fiber.Ctx) error {
	tenantId, _ := strconv.Atoi(c.Query("tenantid"))
	locationid, _ := strconv.Atoi(c.Query("locationid"))
	subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))

	data, err := domain.GetSubCategoryWiseSummary(tenantId, locationid, subcategoryid)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"status":  false,
			"message": "Failed to fetch subcategory-wise summary",
		})
	}

	if data == nil {
		data = []models.SubCategorySummary{}
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Subcategory-wise summary fetched successfully",
		"status":  true,
		"details": data,
	})
}

func GetLocationProductSummary(c *fiber.Ctx) error {
	tenantid, _ := strconv.Atoi(c.Query("tenantid"))
	locationid, _ := strconv.Atoi(c.Query("locationid"))

	result := domain.GetLocationProductSummary(tenantid, locationid)

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusOK,
		"message": "Success",
		"details": result,
	})
}

func GetStockStatementSummary(c *fiber.Ctx) error {
	tenantId, _ := strconv.Atoi(c.Query("tenantid"))
	locationid, _ := strconv.Atoi(c.Query("locationid"))

	data := domain.GetStockStatementSummary(tenantId, locationid)

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": data,
	})
}

func CreateProductDiscount(c *fiber.Ctx) error {

	var data models.ProductDiscount

	if err := c.BodyParser(&data); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{
			"status":  false,
			"code":    http.StatusBadRequest,
			"message": "Invalid JSON body",
		})
	}

	if len(data.Locationid) > 0 {
		data.LocationidStr = strings.Join(data.Locationid, ",")
	}

	data.Status = "Active"

	if err := domain.CreateProductDiscount(&data); err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"status":  false,
			"code":    http.StatusInternalServerError,
			"message": err.Error(),
		})
	}

	return c.Status(http.StatusCreated).JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusCreated,
		"message": "Product discount created successfully",
		"data":    data,
	})
}

func GetProductDiscounts(c *fiber.Ctx) error {

	tid, _ := strconv.Atoi(c.Query("tenantid"))
	lid := c.Query("locationid")
	data := domain.GetProductDiscounts(tid, lid)

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    200,
		"message": "Success",
		"details": data,
	})
}
