package services

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"gauth-central/internal/database"
	"gauth-central/internal/models"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type PostService struct{}

func NewPostService() *PostService {
	return &PostService{}
}

func (s *PostService) CreatePost(
	title string,
	content string,
	keywords string,
	image string,
	video string,
	categoryIDs []string,
	tagIDs []string,
	parentID *uuid.UUID,
	isActive bool,
	isFront bool,
) (*models.Post, error) {
	slug := s.generateUniqueSlug(slugifyPost(title), "")
	if slug == "" {
		return nil, errors.New("slug cannot be empty")
	}

	post := models.Post{
		Title:    title,
		Content:  content,
		Keywords: keywords,
		Image:    image,
		Video:    video,
		Slug:     slug,
		ParentID: parentID,
		IsActive: isActive,
		IsFront:  isFront,
	}

	if len(categoryIDs) > 0 {
		categories, err := s.fetchCategoriesByIDs(categoryIDs)
		if err != nil {
			return nil, err
		}
		post.Categories = categories
	}

	if len(tagIDs) > 0 {
		tags, err := s.fetchTagsByIDs(tagIDs)
		if err != nil {
			return nil, err
		}
		post.Tags = tags
	}

	if err := database.DB.Create(&post).Error; err != nil {
		return nil, err
	}

	return s.GetPostByID(post.ID.String())
}

func (s *PostService) GetAllPosts(onlyActive bool, onlyFront bool, page int, limit int) ([]models.Post, int64, error) {
	var posts []models.Post
	var total int64

	query := database.DB.Model(&models.Post{})
	if onlyActive {
		query = query.Where("is_active = ?", true)
	}
	if onlyFront {
		query = query.Where("is_front = ?", true)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * limit

	if err := query.Preload("Categories").Preload("Tags").
		Order("created_at desc").
		Limit(limit).
		Offset(offset).
		Find(&posts).Error; err != nil {
		return nil, 0, err
	}

	return posts, total, nil
}

func (s *PostService) GetPostByID(id string) (*models.Post, error) {
	var post models.Post
	if err := database.DB.Preload("Categories").Preload("Tags").Where("id = ?", id).First(&post).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("post not found")
		}
		return nil, err
	}
	return &post, nil
}

func (s *PostService) GetPostBySlug(slug string, onlyActive bool) (*models.Post, error) {
	var post models.Post
	query := database.DB.Preload("Categories").Preload("Tags").Where("slug = ?", slug)
	if onlyActive {
		query = query.Where("is_active = ?", true)
	}

	if err := query.First(&post).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("post not found")
		}
		return nil, err
	}
	return &post, nil
}

func (s *PostService) UpdatePost(
	id string,
	title *string,
	content *string,
	keywords *string,
	image *string,
	video *string,
	categoryIDs *[]string,
	tagIDs *[]string,
	parentID *uuid.UUID,
	parentIDSet bool,
	slug *string,
	isActive *bool,
	isFront *bool,
) (*models.Post, error) {
	post, err := s.GetPostByID(id)
	if err != nil {
		return nil, err
	}

	updates := map[string]interface{}{}
	if title != nil {
		updates["title"] = *title
	}
	if content != nil {
		updates["content"] = *content
	}
	if keywords != nil {
		updates["keywords"] = *keywords
	}
	if image != nil {
		updates["image"] = *image
	}
	if video != nil {
		updates["video"] = *video
	}
	if parentIDSet {
		updates["parent_id"] = parentID
	}
	if slug != nil {
		clean := slugifyPost(*slug)
		if clean == "" {
			return nil, errors.New("slug cannot be empty")
		}
		if s.slugExists(clean, id) {
			return nil, errors.New("slug already exists")
		}
		updates["slug"] = clean
	}
	if isActive != nil {
		updates["is_active"] = *isActive
	}
	if isFront != nil {
		updates["is_front"] = *isFront
	}

	if len(updates) > 0 {
		if err := database.DB.Model(post).Updates(updates).Error; err != nil {
			return nil, err
		}
	}

	if categoryIDs != nil {
		categories, err := s.fetchCategoriesByIDs(*categoryIDs)
		if err != nil {
			return nil, err
		}
		if err := database.DB.Model(post).Association("Categories").Replace(categories); err != nil {
			return nil, err
		}
	}

	if tagIDs != nil {
		tags, err := s.fetchTagsByIDs(*tagIDs)
		if err != nil {
			return nil, err
		}
		if err := database.DB.Model(post).Association("Tags").Replace(tags); err != nil {
			return nil, err
		}
	}

	return s.GetPostByID(id)
}

func (s *PostService) DeletePost(id string) error {
	result := database.DB.Delete(&models.Post{}, "id = ?", id)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return errors.New("post not found")
	}
	return nil
}

func (s *PostService) fetchCategoriesByIDs(categoryIDs []string) ([]models.PostCategory, error) {
	var categories []models.PostCategory
	if err := database.DB.Where("id IN ?", categoryIDs).Find(&categories).Error; err != nil {
		return nil, err
	}
	if len(categories) != len(categoryIDs) {
		return nil, errors.New("one or more categories not found")
	}
	return categories, nil
}

func (s *PostService) fetchTagsByIDs(tagIDs []string) ([]models.PostTag, error) {
	var tags []models.PostTag
	if err := database.DB.Where("id IN ?", tagIDs).Find(&tags).Error; err != nil {
		return nil, err
	}
	if len(tags) != len(tagIDs) {
		return nil, errors.New("one or more tags not found")
	}
	return tags, nil
}

func (s *PostService) generateUniqueSlug(baseSlug string, excludeID string) string {
	slug := baseSlug
	counter := 1
	for s.slugExists(slug, excludeID) {
		slug = fmt.Sprintf("%s-%d", baseSlug, counter)
		counter++
	}
	return slug
}

func (s *PostService) slugExists(slug string, excludeID string) bool {
	var count int64
	query := database.DB.Model(&models.Post{}).Where("slug = ?", slug)
	if excludeID != "" {
		query = query.Where("id <> ?", excludeID)
	}
	query.Count(&count)
	return count > 0
}

func slugifyPost(input string) string {
	clean := strings.TrimSpace(input)
	if clean == "" {
		return ""
	}

	replacer := strings.NewReplacer(
		"ı", "i",
		"İ", "i",
		"ş", "s",
		"Ş", "s",
		"ğ", "g",
		"Ğ", "g",
		"ç", "c",
		"Ç", "c",
		"ö", "o",
		"Ö", "o",
		"ü", "u",
		"Ü", "u",
	)

	clean = strings.ToLower(replacer.Replace(clean))
	re := regexp.MustCompile(`[^a-z0-9]+`)
	clean = re.ReplaceAllString(clean, "-")
	clean = strings.Trim(clean, "-")
	if clean == "" {
		return "post"
	}
	return clean
}
