// internal/services/category_service.go
package services

import (
	"errors"
	"lost-and-found/internal/models"
	"lost-and-found/internal/repositories"
	"strings"

	"gorm.io/gorm"
)

type CategoryService struct {
	categoryRepo *repositories.CategoryRepository
	auditLogRepo *repositories.AuditLogRepository
}

func NewCategoryService(db *gorm.DB) *CategoryService {
	return &CategoryService{
		categoryRepo: repositories.NewCategoryRepository(db),
		auditLogRepo: repositories.NewAuditLogRepository(db),
	}
}

// CreateCategoryRequest represents category creation data
type CreateCategoryRequest struct {
	Name        string `json:"name" binding:"required"`
	Description string `json:"description"`
}

// UpdateCategoryRequest represents category update data
type UpdateCategoryRequest struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

// GetAllCategories gets all categories
func (s *CategoryService) GetAllCategories() ([]models.CategoryResponse, error) {
	return s.categoryRepo.GetAllWithItemCount()
}

// GetCategoryByID gets category by ID
func (s *CategoryService) GetCategoryByID(id uint) (*models.Category, error) {
	return s.categoryRepo.FindByID(id)
}

// GetCategoryBySlug gets category by slug
func (s *CategoryService) GetCategoryBySlug(slug string) (*models.Category, error) {
	return s.categoryRepo.FindBySlug(slug)
}

// CreateCategory creates a new category (admin only)
func (s *CategoryService) CreateCategory(adminID uint, req CreateCategoryRequest, ipAddress, userAgent string) (*models.Category, error) {
	// Generate slug from name
	slug := s.generateSlug(req.Name)

	// Check if slug already exists
	existing, _ := s.categoryRepo.FindBySlug(slug)
	if existing != nil {
		return nil, errors.New("category with similar name already exists")
	}

	category := &models.Category{
		Name:        req.Name,
		Slug:        slug,
		Description: req.Description,
	}

	if err := s.categoryRepo.Create(category); err != nil {
		return nil, errors.New("failed to create category")
	}

	// Log audit
	s.auditLogRepo.Log(&adminID, models.ActionCreate, models.EntityCategory, &category.ID,
		"Category created: "+category.Name, ipAddress, userAgent)

	return category, nil
}

// UpdateCategory updates a category (admin only)
func (s *CategoryService) UpdateCategory(adminID, categoryID uint, req UpdateCategoryRequest, ipAddress, userAgent string) (*models.Category, error) {
	category, err := s.categoryRepo.FindByID(categoryID)
	if err != nil {
		return nil, err
	}

	// Update fields
	if req.Name != "" {
		category.Name = req.Name
		category.Slug = s.generateSlug(req.Name)
	}
	if req.Description != "" {
		category.Description = req.Description
	}

	if err := s.categoryRepo.Update(category); err != nil {
		return nil, errors.New("failed to update category")
	}

	// Log audit
	s.auditLogRepo.Log(&adminID, models.ActionUpdate, models.EntityCategory, &categoryID,
		"Category updated: "+category.Name, ipAddress, userAgent)

	return category, nil
}

// DeleteCategory deletes a category (admin only)
func (s *CategoryService) DeleteCategory(adminID, categoryID uint, ipAddress, userAgent string) error {
	category, err := s.categoryRepo.FindByID(categoryID)
	if err != nil {
		return err
	}

	// Check if category has items
	_, count, err := s.categoryRepo.GetCategoryWithItemCount(categoryID)
	if err != nil {
		return err
	}
	if count > 0 {
		return errors.New("cannot delete category with existing items")
	}

	if err := s.categoryRepo.Delete(categoryID); err != nil {
		return errors.New("failed to delete category")
	}

	// Log audit
	s.auditLogRepo.Log(&adminID, models.ActionDelete, models.EntityCategory, &categoryID,
		"Category deleted: "+category.Name, ipAddress, userAgent)

	return nil
}

// generateSlug generates URL-friendly slug from name
func (s *CategoryService) generateSlug(name string) string {
	slug := strings.ToLower(name)
	slug = strings.ReplaceAll(slug, " ", "_")
	slug = strings.ReplaceAll(slug, "/", "_")
	// Remove special characters
	validChars := "abcdefghijklmnopqrstuvwxyz0123456789_"
	result := ""
	for _, char := range slug {
		if strings.ContainsRune(validChars, char) {
			result += string(char)
		}
	}
	return result
}