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,
			"name":     space.Name,
		}
	}

	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
}

// CreateStorageSpace creates a new storage space
func (h *StorageHandler) CreateStorageSpace(c echo.Context) error {
	ctx := context.Background()
	
	// Parse request body
	type StorageSpaceRequest struct {
		Name     string `json:"name"`
		Location string `json:"location"`
		Parent   *struct {
			Valid bool  `json:"valid"`
			Int64 int64 `json:"int64"`
		} `json:"parent"`
	}
	
	var req StorageSpaceRequest
	if err := c.Bind(&req); err != nil {
		h.logger.Warn("Invalid request body", zap.Error(err))
		return c.JSON(http.StatusBadRequest, map[string]string{
			"error": "Invalid request body",
		})
	}
	
	// Convert parent to sql.NullInt64
	var parent sql.NullInt64
	if req.Parent != nil && req.Parent.Valid {
		parent.Valid = true
		parent.Int64 = req.Parent.Int64
		
		// Verify parent exists
		_, err := h.db.GetStorageSpaceByID(ctx, parent.Int64)
		if err != nil {
			if err == sql.ErrNoRows {
				return c.JSON(http.StatusBadRequest, map[string]string{
					"error": "Parent storage space not found",
				})
			}
			h.logger.Error("Failed to fetch parent storage space", zap.Error(err))
			return c.JSON(http.StatusInternalServerError, map[string]string{
				"error": "Failed to verify parent storage space",
			})
		}
	}
	
	// Convert location to sql.NullString
	var location sql.NullString
	if req.Location != "" {
		location.Valid = true
		location.String = req.Location
	}
	
	// Create storage space
	err := h.db.CreateStorageSpace(ctx, db.CreateStorageSpaceParams{
		Parent:   parent,
		Location: location,
		Name:     req.Name,
	})
	
	if err != nil {
		h.logger.Error("Failed to create storage space", zap.Error(err))
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "Failed to create storage space",
		})
	}
	
	return c.JSON(http.StatusCreated, map[string]string{
		"message": "Storage space created successfully",
	})
}

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