package api

import (
	"context"
	"database/sql"
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"
	"go.uber.org/zap"

	db "git.entr0py.de/garionion/inventory/database/sqlite/generated"
)

type StorageHandler struct {
	db     *db.Queries
	logger *zap.Logger
}

func NewStorageHandler(db *db.Queries, logger *zap.Logger) *StorageHandler {
	return &StorageHandler{
		db:     db,
		logger: logger,
	}
}

// GetStorageSpaces returns all storage spaces
func (h *StorageHandler) GetStorageSpaces(c echo.Context) error {
	ctx := context.Background()

	storageSpaces, err := h.db.GetAllStorageSpaces(ctx)
	if err != nil {
		h.logger.Error("Failed to fetch storage spaces", zap.Error(err))
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "Failed to fetch storage spaces",
		})
	}

	// Convert to a format suitable for the frontend
	result := make([]map[string]interface{}, len(storageSpaces))
	for i, space := range storageSpaces {
		result[i] = map[string]interface{}{
			"id":       space.ID,
			"parent":   space.Parent,
			"location": space.Location,
		}
	}

	return c.JSON(http.StatusOK, result)
}

// GetObjectsInStorage returns all objects in a specific storage space
func (h *StorageHandler) GetObjectsInStorage(c echo.Context) error {
	ctx := context.Background()

	// Parse storage ID from URL
	storageIDStr := c.Param("id")
	storageID, err := strconv.ParseInt(storageIDStr, 10, 64)
	if err != nil {
		h.logger.Warn("Invalid storage ID", zap.String("id", storageIDStr))
		return c.JSON(http.StatusBadRequest, map[string]string{
			"error": "Invalid storage ID",
		})
	}

	// Check if storage exists
	_, err = h.db.GetStorageSpaceByID(ctx, storageID)
	if err != nil {
		if err == sql.ErrNoRows {
			return c.JSON(http.StatusNotFound, map[string]string{
				"error": "Storage space not found",
			})
		}
		h.logger.Error("Failed to fetch storage space", zap.Error(err))
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "Failed to fetch storage space",
		})
	}

	// Get objects in this storage
	objects, err := h.db.GetObjectsByStorageID(ctx, storageID)
	if err != nil {
		h.logger.Error("Failed to fetch objects in storage", zap.Error(err))
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "Failed to fetch objects in storage",
		})
	}

	// Convert to a format suitable for the frontend
	result := make([]map[string]interface{}, len(objects))
	for i, obj := range objects {
		result[i] = map[string]interface{}{
			"id":             obj.ID,
			"name":           obj.Name,
			"storagespaceId": obj.StoragespaceID,
			"description":    obj.Description.String,
			"serialnumber":   obj.Serialnumber.String,
		}
	}

	return c.JSON(http.StatusOK, result)
}

// GetStorageHierarchyObjects returns all objects in a storage hierarchy
func (h *StorageHandler) GetStorageHierarchyObjects(c echo.Context) error {
	ctx := context.Background()

	// Parse root storage ID from URL
	rootStorageIDStr := c.Param("id")
	rootStorageID, err := strconv.ParseInt(rootStorageIDStr, 10, 64)
	if err != nil {
		h.logger.Warn("Invalid storage ID", zap.String("id", rootStorageIDStr))
		return c.JSON(http.StatusBadRequest, map[string]string{
			"error": "Invalid storage ID",
		})
	}

	// Check if root storage exists
	_, err = h.db.GetStorageSpaceByID(ctx, rootStorageID)
	if err != nil {
		if err == sql.ErrNoRows {
			return c.JSON(http.StatusNotFound, map[string]string{
				"error": "Storage space not found",
			})
		}
		h.logger.Error("Failed to fetch storage space", zap.Error(err))
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "Failed to fetch storage space",
		})
	}

	// Get all storage spaces to build the hierarchy
	allStorageSpaces, err := h.db.GetAllStorageSpaces(ctx)
	if err != nil {
		h.logger.Error("Failed to fetch all storage spaces", zap.Error(err))
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "Failed to fetch storage hierarchy",
		})
	}

	// Build a list of all storage IDs in the hierarchy
	storageIDs := []int64{rootStorageID}
	storageIDs = append(storageIDs, h.getChildStorageIDs(allStorageSpaces, rootStorageID)...)

	// Get objects for all storage spaces in the hierarchy
	var allObjects []map[string]interface{}

	for _, storageID := range storageIDs {
		// Get objects in this storage
		objects, err := h.db.GetObjectsByStorageID(ctx, storageID)
		if err != nil {
			h.logger.Error("Failed to fetch objects in storage",
				zap.Int64("storageID", storageID),
				zap.Error(err))
			continue // Skip this storage if there's an error
		}

		// Convert to a format suitable for the frontend
		for _, obj := range objects {
			allObjects = append(allObjects, map[string]interface{}{
				"id":             obj.ID,
				"name":           obj.Name,
				"storagespaceId": obj.StoragespaceID,
				"description":    obj.Description.String,
				"serialnumber":   obj.Serialnumber.String,
			})
		}
	}

	return c.JSON(http.StatusOK, allObjects)
}

// Helper function to recursively get all child storage IDs
func (h *StorageHandler) getChildStorageIDs(spaces []db.Storagespace, parentID int64) []int64 {
	var childIDs []int64

	for _, space := range spaces {
		if space.Parent.Valid && space.Parent.Int64 == parentID {
			childIDs = append(childIDs, space.ID)
			// Recursively get children of this child
			childIDs = append(childIDs, h.getChildStorageIDs(spaces, space.ID)...)
		}
	}

	return childIDs
}

// RegisterRoutes registers all storage-related routes
func (h *StorageHandler) RegisterRoutes(g *echo.Group) {
	g.GET("/storageSpaces", h.GetStorageSpaces)
	g.GET("/storageSpaces/:id/objects", h.GetObjectsInStorage)
	g.GET("/storageSpaces/:id/hierarchy/objects", h.GetStorageHierarchyObjects)
}