package forum

import (
	"fmt"
	"strings"
	"time"

	"dk/internal/database"
)

// Forum represents a forum post or thread in the game
type Forum struct {
	ID       int
	Posted   int64
	LastPost int64
	Author   int
	Parent   int
	Replies  int
	Title    string
	Content  string
}

// New creates a new Forum with sensible defaults
func New() *Forum {
	now := time.Now().Unix()
	return &Forum{
		Posted:   now,
		LastPost: now,
		Author:   0,
		Parent:   0,
		Replies:  0,
		Title:    "",
		Content:  "",
	}
}

// Validate checks if forum has valid values
func (f *Forum) Validate() error {
	if strings.TrimSpace(f.Title) == "" {
		return fmt.Errorf("forum title cannot be empty")
	}
	if strings.TrimSpace(f.Content) == "" {
		return fmt.Errorf("forum content cannot be empty")
	}
	if f.Posted <= 0 {
		return fmt.Errorf("forum Posted timestamp must be positive")
	}
	if f.LastPost <= 0 {
		return fmt.Errorf("forum LastPost timestamp must be positive")
	}
	if f.Parent < 0 {
		return fmt.Errorf("forum Parent cannot be negative")
	}
	if f.Replies < 0 {
		return fmt.Errorf("forum Replies cannot be negative")
	}
	return nil
}

// CRUD operations
func (f *Forum) Delete() error {
	return database.Exec("DELETE FROM forum WHERE id = %d", f.ID)
}

func (f *Forum) Insert() error {
	id, err := database.Insert("forum", f, "ID")
	if err != nil {
		return err
	}
	f.ID = int(id)
	return nil
}

// Query functions
func Find(id int) (*Forum, error) {
	var forum Forum
	err := database.Get(&forum, "SELECT * FROM forum WHERE id = %d", id)
	if err != nil {
		return nil, fmt.Errorf("forum post with ID %d not found", id)
	}
	return &forum, nil
}

func All() ([]*Forum, error) {
	var forums []*Forum
	err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC")
	return forums, err
}

func Threads() ([]*Forum, error) {
	var forums []*Forum
	err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY newpostdate DESC, id DESC")
	return forums, err
}

func ByParent(parentID int) ([]*Forum, error) {
	var forums []*Forum
	if parentID > 0 {
		// Replies sorted chronologically
		err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY postdate ASC, id ASC", parentID)
		return forums, err
	} else {
		// Threads sorted by last activity
		err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY newpostdate DESC, id DESC", parentID)
		return forums, err
	}
}

func ByAuthor(authorID int) ([]*Forum, error) {
	var forums []*Forum
	err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY postdate DESC, id DESC", authorID)
	return forums, err
}

func Recent(limit int) ([]*Forum, error) {
	var forums []*Forum
	err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC LIMIT %d", limit)
	return forums, err
}

func Search(term string) ([]*Forum, error) {
	var forums []*Forum
	searchTerm := "%" + term + "%"
	err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY newpostdate DESC, id DESC", searchTerm, searchTerm)
	return forums, err
}

func Since(since int64) ([]*Forum, error) {
	var forums []*Forum
	err := database.Select(&forums, "SELECT * FROM forum WHERE newpostdate >= %d ORDER BY newpostdate DESC, id DESC", since)
	return forums, err
}

// Helper methods
func (f *Forum) PostedTime() time.Time {
	return time.Unix(f.Posted, 0)
}

func (f *Forum) LastPostTime() time.Time {
	return time.Unix(f.LastPost, 0)
}

func (f *Forum) SetPostedTime(t time.Time) {
	f.Posted = t.Unix()
}

func (f *Forum) SetLastPostTime(t time.Time) {
	f.LastPost = t.Unix()
}

func (f *Forum) IsThread() bool {
	return f.Parent == 0
}

func (f *Forum) IsReply() bool {
	return f.Parent > 0
}

func (f *Forum) HasReplies() bool {
	return f.Replies > 0
}

func (f *Forum) IsRecentActivity() bool {
	return time.Since(f.LastPostTime()) < 24*time.Hour
}

func (f *Forum) ActivityAge() time.Duration {
	return time.Since(f.LastPostTime())
}

func (f *Forum) PostAge() time.Duration {
	return time.Since(f.PostedTime())
}

func (f *Forum) IsAuthor(userID int) bool {
	return f.Author == userID
}

func (f *Forum) Preview(maxLength int) string {
	if len(f.Content) <= maxLength {
		return f.Content
	}

	if maxLength < 3 {
		return f.Content[:maxLength]
	}

	return f.Content[:maxLength-3] + "..."
}

func (f *Forum) WordCount() int {
	if f.Content == "" {
		return 0
	}

	// Simple word count by splitting on whitespace
	words := 0
	inWord := false

	for _, char := range f.Content {
		if char == ' ' || char == '\t' || char == '\n' || char == '\r' {
			if inWord {
				words++
				inWord = false
			}
		} else {
			inWord = true
		}
	}

	if inWord {
		words++
	}

	return words
}

func (f *Forum) Length() int {
	return len(f.Content)
}

func (f *Forum) Contains(term string) bool {
	lowerTerm := strings.ToLower(term)
	return strings.Contains(strings.ToLower(f.Title), lowerTerm) ||
		strings.Contains(strings.ToLower(f.Content), lowerTerm)
}

func (f *Forum) UpdateLastPost() {
	f.LastPost = time.Now().Unix()
}

func (f *Forum) IncrementReplies() {
	f.Replies++
}

func (f *Forum) DecrementReplies() {
	if f.Replies > 0 {
		f.Replies--
	}
}

func (f *Forum) GetReplies() ([]*Forum, error) {
	return ByParent(f.ID)
}

func (f *Forum) GetThread() (*Forum, error) {
	if f.IsThread() {
		return f, nil
	}
	return Find(f.Parent)
}
