package main

import (
	crand "crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	configs "ares/config"
	database "ares/database/config"
	"ares/database/models"

	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var nonSlugChars = regexp.MustCompile(`[^a-z0-9\-]+`)
var multiDash = regexp.MustCompile(`-+`)

func randHex(n int) string {
	b := make([]byte, n)
	_, _ = crand.Read(b)
	return hex.EncodeToString(b)[:n]
}

func ensureUploadsDir() error {
	p := "./uploads/posts"
	return os.MkdirAll(p, 0755)
}

func slugify(s string) string {
	repl := strings.NewReplacer(
		"ç", "c", "Ç", "c",
		"ğ", "g", "Ğ", "g",
		"ı", "i", "İ", "i",
		"ö", "o", "Ö", "o",
		"ş", "s", "Ş", "s",
		"ü", "u", "Ü", "u",
	)
	s = repl.Replace(strings.TrimSpace(strings.ToLower(s)))
	s = strings.ReplaceAll(s, " ", "-")
	s = nonSlugChars.ReplaceAllString(s, "-")
	s = multiDash.ReplaceAllString(s, "-")
	s = strings.Trim(s, "-")
	if s == "" {
		return "item"
	}
	return s
}

func ensureUniqueCategorySlug(db *gorm.DB, base string) string {
	slug := base
	i := 1
	for {
		var count int64
		_ = db.Model(&models.Category{}).Where("slug = ?", slug).Count(&count).Error
		if count == 0 {
			return slug
		}
		i++
		slug = fmt.Sprintf("%s-%d", base, i)
	}
}

func ensureUniquePostSlug(db *gorm.DB, base string) string {
	slug := base
	i := 1
	for {
		var count int64
		_ = db.Model(&models.Post{}).Where("slug = ?", slug).Count(&count).Error
		if count == 0 {
			return slug
		}
		i++
		slug = fmt.Sprintf("%s-%d", base, i)
	}
}

func downloadImage(destDir string, idx int) (string, error) {
	url := fmt.Sprintf("https://picsum.photos/1200/800?random=%d", idx)
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	filename := fmt.Sprintf("post_%d_%s.jpg", idx, randHex(6))
	outPath := filepath.Join(destDir, filename)
	out, err := os.Create(outPath)
	if err != nil {
		return "", err
	}
	defer out.Close()
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return "", err
	}
	return "/uploads/posts/" + filename, nil
}

func main() {
	fmt.Println("Seeder starting...")

	// load config to get DB_URL
	configs.LoadConfig()

	// ensure uploads dir
	if err := ensureUploadsDir(); err != nil {
		fmt.Println("failed to create uploads dir:", err)
		os.Exit(1)
	}

	// Require DB_URL / configured DB. Do not fallback to sqlite.
	database.ConnectDB()
	if database.DB == nil {
		fmt.Println("Database not configured or connection failed. Please set DB_URL in .env and ensure database is reachable.")
		os.Exit(1)
	}
	// set GORM logger to Silent for seeding to reduce noise
	var db *gorm.DB = database.DB.Session(&gorm.Session{Logger: logger.Default.LogMode(logger.Silent)})
	fmt.Println("Using configured DB")

	// auto-migrate minimal models used
	err := db.AutoMigrate(&models.Category{}, &models.Tag{}, &models.Post{}, &models.Comment{})
	if err != nil {
		fmt.Println("AutoMigrate failed:", err)
		os.Exit(1)
	}

	rand.Seed(time.Now().UnixNano())

	// create categories
	cats := []models.Category{}
	catNames := []string{"Teknoloji", "Yazilim", "Guncel", "Yasam", "Egitim", "Spor", "Saglik", "Finans"}
	for _, n := range catNames {
		baseSlug := slugify(n)
		var c models.Category
		if err := db.Where("title = ?", n).First(&c).Error; err == nil {
			// Ensure old records have a valid unique slug.
			if strings.TrimSpace(c.Slug) == "" {
				c.Slug = ensureUniqueCategorySlug(db, baseSlug)
				_ = db.Save(&c).Error
			}
			cats = append(cats, c)
			continue
		}

		c = models.Category{
			Title:       n,
			Slug:        ensureUniqueCategorySlug(db, baseSlug),
			Description: fmt.Sprintf("%s kategorisi seed verisi", n),
		}
		res := db.Create(&c)
		if res.Error != nil {
			fmt.Println("Failed to create category", n, ":", res.Error)
			continue
		}
		cats = append(cats, c)
		fmt.Println("Created category:", c.Title, "slug:", c.Slug)
	}

	// create tags
	tags := []models.Tag{}
	tagNames := []string{"go", "fiber", "backend", "mysql", "redis", "jwt", "api", "docker", "cloud", "devops", "security", "testing"}
	for _, n := range tagNames {
		var t models.Tag
		if err := db.Where("name = ?", n).First(&t).Error; err == nil {
			tags = append(tags, t)
			continue
		}
		t = models.Tag{Name: n}
		res := db.Create(&t)
		if res.Error != nil {
			fmt.Println("Failed to create tag", n, ":", res.Error)
			continue
		}
		tags = append(tags, t)
		fmt.Println("Created tag:", t.Name)
	}

	// create posts
	dest := "./uploads/posts"
	targetPosts := 39
	for i := 1; i <= targetPosts; i++ {
		imgPath, err := downloadImage(dest, i)
		if err != nil {
			fmt.Println("image download failed for", i, "— using fallback path. err:", err)
			imgPath = fmt.Sprintf("/uploads/posts/fallback_%d.jpg", i)
		}

		title := fmt.Sprintf("Seed Post %d", i)
		baseSlug := slugify(title)
		uniqueSlug := ensureUniquePostSlug(db, baseSlug)
		p := models.Post{
			Title:   title,
			Slug:    uniqueSlug,
			Content: fmt.Sprintf("Bu bir test icerigidir. Gonderi numarasi %d.", i),
		}

		// randomly attach 1-2 categories
		nc := rand.Intn(2) + 1
		permCats := rand.Perm(len(cats))[:nc]
		for _, idx := range permCats {
			p.Categories = append(p.Categories, cats[idx])
		}
		// attach 1-3 tags
		nx := rand.Intn(3) + 1
		permTags := rand.Perm(len(tags))[:nx]
		for _, idx := range permTags {
			p.Tags = append(p.Tags, tags[idx])
		}

		// assign image paths (seed: same path for all sizes)
		p.Images = imgPath
		p.ImagesMid = imgPath
		p.ImagesMin = imgPath

		res := db.Create(&p)
		if res.Error != nil {
			fmt.Println("Failed to create post", title, ":", res.Error)
		} else {
			fmt.Println("Created post", p.Title, "slug:", p.Slug)
		}
	}

	fmt.Printf("Seeding done — %d posts targeted.\n", targetPosts)
	// print reminder where images are
	fmt.Println("Images saved to ./uploads/posts — check files.")
}
