package handlers

import (
	"bookhoard/internal/database"
	"bookhoard/internal/services"
	"context"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgtype"
	"github.com/labstack/echo/v5"
)

type LibraryHandler struct {
	db             *database.Queries
	libraryService *services.LibraryService
}

func NewLibraryHandler(db *database.Queries) *LibraryHandler {
	return &LibraryHandler{
		db:             db,
		libraryService: services.NewLibraryService(db),
	}
}

// Request/Response types
type CreateLibraryRequest struct {
	Name        string `json:"name" validate:"required"`
	Description string `json:"description"`
	Type        string `json:"type" validate:"required"`
}

type UpdateLibraryRequest struct {
	Name        string `json:"name" validate:"required"`
	Description string `json:"description"`
}

type AddLibraryFolderRequest struct {
	FolderPath string `json:"folder_path" validate:"required"`
}

type SetLibraryVisibilityRequest struct {
	LibraryID string `json:"library_id" validate:"required"`
	IsVisible bool   `json:"is_visible"`
}

// GetLibraryTypes retrieves all available library types
func (h *LibraryHandler) GetLibraryTypes(c *echo.Context) error {
	types, err := h.libraryService.GetLibraryTypes(c.Request().Context())
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}
	return c.JSON(http.StatusOK, types)
}

// CreateLibrary creates a new library
func (h *LibraryHandler) CreateLibrary(c *echo.Context) error {
	user := c.Get("user").(database.Users)
	userUUID := user.ID.Bytes

	var req CreateLibraryRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	library, err := h.libraryService.CreateLibrary(
		c.Request().Context(),
		req.Name,
		req.Description,
		req.Type,
		pgtype.UUID{Bytes: userUUID, Valid: true},
	)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

	return c.JSON(http.StatusCreated, library)
}

// GetLibrary retrieves a specific library
func (h *LibraryHandler) GetLibrary(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	library, err := h.libraryService.GetLibrary(c.Request().Context(), libraryID)
	if err != nil {
		return c.JSON(http.StatusNotFound, map[string]string{"error": "library not found"})
	}

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

// ListLibraries retrieves all libraries (admin only)
func (h *LibraryHandler) ListLibraries(c *echo.Context) error {
	libraries, err := h.libraryService.ListLibraries(c.Request().Context())
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"data": libraries})
}

// GetUserVisibleLibraries retrieves libraries visible to the current user
func (h *LibraryHandler) GetUserVisibleLibraries(c *echo.Context) error {
	user := MustGetAuthenticatedUser(c)

	libraries, err := h.libraryService.GetUserVisibleLibraries(c.Request().Context(), user.ID)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

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

// UpdateLibrary updates an existing library
func (h *LibraryHandler) UpdateLibrary(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	var req UpdateLibraryRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	library, err := h.libraryService.UpdateLibrary(
		c.Request().Context(),
		libraryID,
		req.Name,
		req.Description,
	)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

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

// DeleteLibrary deletes a library
func (h *LibraryHandler) DeleteLibrary(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	err = h.libraryService.DeleteLibrary(c.Request().Context(), libraryID)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

	return c.NoContent(http.StatusNoContent)
}

// AddLibraryFolder adds a folder to a library
func (h *LibraryHandler) AddLibraryFolder(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	var req AddLibraryFolderRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	// Path traversal protection - detect and block .. in path
	if strings.Contains(req.FolderPath, "..") {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "path traversal not allowed"})
	}

	// Clean the path to remove any redundant separators or . references
	cleanPath := filepath.Clean(req.FolderPath)

	// Validate that folder path exists and is accessible
	fileInfo, err := os.Stat(cleanPath)
	if err != nil {
		if os.IsNotExist(err) {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "folder path does not exist"})
		}
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "folder is not accessible"})
	}

	// Ensure it's actually a directory, not a file
	if !fileInfo.IsDir() {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "path must be a directory"})
	}

	folder, err := h.libraryService.AddLibraryFolder(
		c.Request().Context(),
		libraryID,
		cleanPath,
	)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

	return c.JSON(http.StatusCreated, folder)
}

// GetLibraryFolders retrieves all folders for a library
func (h *LibraryHandler) GetLibraryFolders(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	folders, err := h.libraryService.GetLibraryFolders(c.Request().Context(), libraryID)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

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

// DeleteLibraryFolder removes a folder from a library
func (h *LibraryHandler) DeleteLibraryFolder(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	var req AddLibraryFolderRequest // reuse same structure for folder_path
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	err = h.libraryService.DeleteLibraryFolder(c.Request().Context(), libraryID, req.FolderPath)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

	return c.NoContent(http.StatusNoContent)
}

// BrowseDirectories returns directory listings for folder browser UI
func (h *LibraryHandler) BrowseDirectories(c *echo.Context) error {
	path := c.QueryParam("path")
	if path == "" {
		path = "/" // Start from root
	}

	dirs, currentPath, parentPath, err := h.libraryService.BrowseDirectories(c.Request().Context(), path)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"current_path": currentPath,
		"parent_path":  parentPath,
		"directories":  dirs,
	})
}

// SetLibraryVisibility sets library visibility for a user
func (h *LibraryHandler) SetLibraryVisibility(c *echo.Context) error {
	user := c.Get("user").(database.Users)

	var req SetLibraryVisibilityRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	libraryID, err := parseUUID(req.LibraryID)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	visibility, err := h.libraryService.SetLibraryVisibility(
		c.Request().Context(),
		user.ID,
		libraryID,
		req.IsVisible,
	)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

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

// GetLibraryStats retrieves statistics for a library
func (h *LibraryHandler) GetLibraryStats(c *echo.Context) error {
	libraryID, err := parseUUID(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
	}

	stats, err := h.libraryService.GetLibraryStats(c.Request().Context(), libraryID)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

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

// Helper function
func parseUUID(uuidStr string) (pgtype.UUID, error) {
	parsedUUID, err := uuid.Parse(uuidStr)
	if err != nil {
		return pgtype.UUID{}, err
	}
	return pgtype.UUID{Bytes: parsedUUID, Valid: true}, nil
}

// GetUserVisibleLibrariesData returns libraries for SSR (not JSON response)
func (h *LibraryHandler) GetUserVisibleLibrariesData(ctx context.Context, userID pgtype.UUID) ([]database.GetUserVisibleLibrariesRow, error) {
	return h.libraryService.GetUserVisibleLibraries(ctx, userID)
}

// GetLibraryTypeData returns all library types for SSR (not JSON response)
func (h *LibraryHandler) GetLibraryTypeData(ctx context.Context) ([]database.LibraryTypes, error) {
	return h.libraryService.GetLibraryTypes(ctx)
}

// ListLibrariesData returns all libraries for SSR (admin only, not JSON response)
func (h *LibraryHandler) ListLibrariesData(ctx context.Context) ([]database.ListLibrariesRow, error) {
	return h.libraryService.ListLibraries(ctx)
}
