package services

import (
	"bookhoard/internal/database"
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/jackc/pgx/v5/pgtype"
)

type LibraryService struct {
	db *database.Queries
}

func NewLibraryService(db *database.Queries) *LibraryService {
	return &LibraryService{
		db: db,
	}
}

// Library type definitions and file extensions
const (
	LibraryTypeEbooks = "ebooks"
	LibraryTypeComics = "comics"
	LibraryTypeManga  = "manga"
)

var AllowedExtensions = map[string][]string{
	LibraryTypeEbooks: {".epub", ".pdf", ".mobi", ".azw", ".azw3", ".txt", ".rtf", ".doc", ".docx", ".lit", ".fb2", ".pdb"},
	LibraryTypeComics: {".cbz", ".cbr", ".cb7", ".cbt", ".epub", ".pdf"},
	LibraryTypeManga:  {".cbz", ".cbr", ".epub", ".pdf", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".avif", ".tiff", ".tif"},
}

var MimeTypes = map[string]string{
	// Images (manga)
	".png":  "image/png",
	".jpg":  "image/jpeg",
	".jpeg": "image/jpeg",
	".gif":  "image/gif",
	".bmp":  "image/bmp",
	".webp": "image/webp",
	".avif": "image/avif",
	".tiff": "image/tiff",
	".tif":  "image/tiff",
	// Comics
	".cbz": "application/vnd.comicbook+zip",
	".cbr": "application/vnd.comicbook-rar",
	".cb7": "application/x-cb7",
	".cbt": "application/x-cbt",
	// Ebooks
	".epub": "application/epub+zip",
	".pdf":  "application/pdf",
	".mobi": "application/x-mobipocket-ebook",
	".azw":  "application/vnd.amazon.ebook",
	".azw3": "application/vnd.amazon.ebook",
	".txt":  "text/plain",
	".rtf":  "application/rtf",
	".doc":  "application/msword",
	".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
	".lit":  "application/x-msreader",
	".fb2":  "application/x-fictionbook+xml",
	".pdb":  "application/vnd.palm",
}

// GetLibraryTypes retrieves all available library types
func (s *LibraryService) GetLibraryTypes(ctx context.Context) ([]database.LibraryTypes, error) {
	return s.db.GetLibraryTypes(ctx)
}

// CreateLibrary creates a new library with the given parameters
func (s *LibraryService) CreateLibrary(ctx context.Context, name, description, libraryType string, adminID pgtype.UUID) (*database.Libraries, error) {
	// Get library type ID
	libType, err := s.db.GetLibraryTypeByName(ctx, libraryType)
	if err != nil {
		return nil, fmt.Errorf("invalid library type: %w", err)
	}

	// Create library
	library, err := s.db.CreateLibrary(ctx, database.CreateLibraryParams{
		Name:             name,
		Description:      pgtype.Text{String: description, Valid: true},
		LibraryTypeID:    libType.ID,
		CreatedByAdminID: adminID,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create library: %w", err)
	}

	return &library, nil
}

// GetLibrary retrieves a library by ID with type information
func (s *LibraryService) GetLibrary(ctx context.Context, libraryID pgtype.UUID) (*database.GetLibraryRow, error) {
	library, err := s.db.GetLibrary(ctx, libraryID)
	if err != nil {
		return nil, err
	}
	return &library, nil
}

// ListLibraries retrieves all libraries
func (s *LibraryService) ListLibraries(ctx context.Context) ([]database.ListLibrariesRow, error) {
	return s.db.ListLibraries(ctx)
}

// UpdateLibrary updates an existing library
func (s *LibraryService) UpdateLibrary(ctx context.Context, libraryID pgtype.UUID, name, description string) (*database.Libraries, error) {
	library, err := s.db.UpdateLibrary(ctx, database.UpdateLibraryParams{
		ID:          libraryID,
		Name:        name,
		Description: pgtype.Text{String: description, Valid: true},
	})
	if err != nil {
		return nil, err
	}
	return &library, nil
}

// DeleteLibrary deletes a library and all its associated data
func (s *LibraryService) DeleteLibrary(ctx context.Context, libraryID pgtype.UUID) error {
	return s.db.DeleteLibrary(ctx, libraryID)
}

// AddLibraryFolder adds a folder to a library
func (s *LibraryService) AddLibraryFolder(ctx context.Context, libraryID pgtype.UUID, folderPath string) (*database.LibraryFolders, error) {
	folder, err := s.db.AddLibraryFolder(ctx, database.AddLibraryFolderParams{
		LibraryID:  libraryID,
		FolderPath: folderPath,
	})
	if err != nil {
		return nil, err
	}
	return &folder, nil
}

// GetLibraryFolders retrieves all folders for a library
func (s *LibraryService) GetLibraryFolders(ctx context.Context, libraryID pgtype.UUID) ([]database.LibraryFolders, error) {
	return s.db.GetLibraryFolders(ctx, libraryID)
}

// DeleteLibraryFolder removes a folder from a library
func (s *LibraryService) DeleteLibraryFolder(ctx context.Context, libraryID pgtype.UUID, folderPath string) error {
	_, err := s.db.DeleteLibraryFolder(ctx, database.DeleteLibraryFolderParams{
		LibraryID:  libraryID,
		FolderPath: folderPath,
	})
	return err
}

// SetLibraryVisibility sets library visibility for a user
func (s *LibraryService) SetLibraryVisibility(ctx context.Context, userID, libraryID pgtype.UUID, isVisible bool) (*database.LibraryVisibility, error) {
	visibility, err := s.db.SetLibraryVisibility(ctx, database.SetLibraryVisibilityParams{
		UserID:    userID,
		LibraryID: libraryID,
		IsVisible: isVisible,
	})
	if err != nil {
		return nil, err
	}
	return &visibility, nil
}

// GetUserVisibleLibraries retrieves all libraries visible to a user
func (s *LibraryService) GetUserVisibleLibraries(ctx context.Context, userID pgtype.UUID) ([]database.GetUserVisibleLibrariesRow, error) {
	return s.db.GetUserVisibleLibraries(ctx, userID)
}

// IsFileExtensionAllowed checks if a file extension is allowed for a library type
func (s *LibraryService) IsFileExtensionAllowed(libraryType, extension string) bool {
	extensions, exists := AllowedExtensions[libraryType]
	if !exists {
		return false
	}

	for _, ext := range extensions {
		if strings.EqualFold(ext, extension) {
			return true
		}
	}
	return false
}

// GetLibraryFileExtensions returns all allowed file extensions for a library type
func (s *LibraryService) GetLibraryFileExtensions(libraryType string) []string {
	extensions, exists := AllowedExtensions[libraryType]
	if !exists {
		return []string{}
	}
	return extensions
}

// GetLibraryTypeFromFileExtension determines the library type based on file extension
func (s *LibraryService) GetLibraryTypeFromFileExtension(filename string) string {
	ext := strings.ToLower(filepath.Ext(filename))

	for libType, extensions := range AllowedExtensions {
		for _, allowedExt := range extensions {
			if ext == allowedExt {
				return libType
			}
		}
	}

	return ""
}

// ValidateLibraryPath checks if a path is valid for the given library type
func (s *LibraryService) ValidateLibraryPath(libraryType, folderPath string) error {
	// You could add more validation here like:
	// - Check if path exists
	// - Check if path is readable
	// - Validate path format for specific library types
	// - Check for appropriate file structures (e.g., for manga with image folders)

	return nil
}

// GetLibraryStats returns statistics for a library (media count, etc.)
func (s *LibraryService) GetLibraryStats(ctx context.Context, libraryID pgtype.UUID) (map[string]interface{}, error) {
	// For now, return basic info. This can be expanded with more detailed stats
	mediaItems, err := s.db.ListMediaItemsByLibrary(ctx, libraryID)
	if err != nil {
		return nil, err
	}

	return map[string]interface{}{
		"media_count": len(mediaItems),
	}, nil
}

// HasFolders checks if a library has at least one folder configured
func (s *LibraryService) HasFolders(ctx context.Context, libraryID pgtype.UUID) (bool, error) {
	folders, err := s.db.GetLibraryFolders(ctx, libraryID)
	if err != nil {
		return false, fmt.Errorf("failed to check library folders: %w", err)
	}
	return len(folders) > 0, nil
}

// BrowseDirectories lists directories at a given path for folder browser UI
// Returns: (directories, currentPath, parentPath, error)
func (s *LibraryService) BrowseDirectories(ctx context.Context, path string) ([]string, string, string, error) {
	// Security: path traversal protection
	if strings.Contains(path, "..") {
		return nil, "", "", fmt.Errorf("path traversal not allowed")
	}

	cleanPath := filepath.Clean(path)

	// Check if path exists and is accessible
	fileInfo, err := os.Stat(cleanPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, "", "", fmt.Errorf("path does not exist")
		}
		return nil, "", "", fmt.Errorf("path not accessible: %w", err)
	}

	if !fileInfo.IsDir() {
		return nil, "", "", fmt.Errorf("not a directory")
	}

	// Read directory contents
	entries, err := os.ReadDir(cleanPath)
	if err != nil {
		return nil, "", "", fmt.Errorf("failed to read directory: %w", err)
	}

	// Filter only directories
	var dirs []string
	for _, entry := range entries {
		if entry.IsDir() {
			dirs = append(dirs, entry.Name())
		}
	}

	// Get parent path for navigation
	parentPath := filepath.Dir(cleanPath)
	if parentPath == cleanPath {
		parentPath = "" // At root
	}

	return dirs, cleanPath, parentPath, nil
}

// SyncAllowedExtensions syncs the Go AllowedExtensions map into the database.
// This ensures library_types.allowed_extensions stays in sync with the Go source of truth.
func (s *LibraryService) SyncAllowedExtensions(ctx context.Context) {
	for typeName, exts := range AllowedExtensions {
		if err := s.db.SyncLibraryTypeExtensions(ctx, database.SyncLibraryTypeExtensionsParams{
			Name:              typeName,
			AllowedExtensions: exts,
		}); err != nil {
			log.Printf("Warning: failed to sync allowed extensions for library type %s: %v", typeName, err)
		}
	}
}

func (s *LibraryService) ResolveMediaPath(ctx context.Context, libraryID pgtype.UUID, relativePath string) (string, error) {
	// Get library folders for this library
	folders, err := s.db.GetLibraryFolders(ctx, libraryID)
	if err != nil || len(folders) == 0 {
		return "", fmt.Errorf("no library folders found for library")
	}

	// Try each folder - find one where the relative path makes sense
	for _, folder := range folders {
		fullPath := filepath.Join(folder.FolderPath, relativePath)
		if _, err := os.Stat(fullPath); err == nil {
			return fullPath, nil
		}
	}

	// Fallback: use first folder (file might not exist yet during scan)
	if len(folders) > 0 {
		return filepath.Join(folders[0].FolderPath, relativePath), nil
	}

	return "", fmt.Errorf("could not resolve path")
}
