package kinds

import (
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	dmyRe   = regexp.MustCompile(`^(\d{1,2})[./-](\d{1,2})[./-](\d{4}|\d{2})$`)
	dmRe    = regexp.MustCompile(`^(\d{1,2})[./](\d{1,2})$`)
	isoRe   = regexp.MustCompile(`^(\d{4})-(\d{1,2})-(\d{1,2})$`)
	clockRe = regexp.MustCompile(`^(\d{1,2})[:.](\d{2})$`)
)

// months ay adlarının katlanmış başlangıçlarıdır (indeks+1 = ay no): "ekim", "ekimde" ikisi de eşleşir.
var months = []string{"ocak", "subat", "mart", "nisan", "mayis", "haziran", "temmuz", "agustos", "eylul", "ekim", "kasim", "aralik"}

// weekdays uzun adlar önce gelir ("cumartesi" "cuma"dan, "pazartesi" "pazar"dan önce denenir).
var weekdays = []struct {
	name string
	day  time.Weekday
}{
	{"pazartesi", time.Monday}, {"cumartesi", time.Saturday}, {"carsamba", time.Wednesday},
	{"persembe", time.Thursday}, {"sali", time.Tuesday}, {"cuma", time.Friday}, {"pazar", time.Sunday},
}

// ParseDate Türkçe/ISO tarih ifadesini YYYY-MM-DD'ye çevirir. Hesap KODDA yapılır (model tarih
// hesaplamaz): ISO, 05.10.2026 / 5/10/26, 5.10 (bu yıl), bugün/yarın/dün, hafta günü adları ("cuma",
// "cumaya kadar": bugün dahil bir sonraki o gün) ve "ay sonu" desteklenir. now sıfır değerse göreli
// ifadeler çözülmez (yalnızca mutlak tarihler). Çözülemeyen ifade ok=false döner.
func ParseDate(s string, now time.Time) (string, bool) {
	t := Fold(s)
	t = strings.Trim(t, " .,;")
	if t == "" {
		return "", false
	}
	loc := now.Location()
	mk := func(y, m, d int) (string, bool) {
		date := time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC)
		if date.Year() != y || int(date.Month()) != m || date.Day() != d || y < 1900 || y > 2200 {
			return "", false // 31.02 gibi: Go normalleştirir, biz reddederiz
		}
		return date.Format("2006-01-02"), true
	}
	atoi := func(x string) int { n, _ := strconv.Atoi(x); return n }

	if m := isoRe.FindStringSubmatch(t); m != nil {
		return mk(atoi(m[1]), atoi(m[2]), atoi(m[3]))
	}
	if m := dmyRe.FindStringSubmatch(t); m != nil {
		y := atoi(m[3])
		if y < 100 {
			y += 2000
		}
		return mk(y, atoi(m[2]), atoi(m[1]))
	}
	// "15 ekim", "15 Ekim 2026" (yıl yoksa bu yıl; göreli çözüm için now gerekir)
	if f := strings.Fields(t); len(f) >= 2 && len(f) <= 3 {
		if d, err := strconv.Atoi(f[0]); err == nil {
			for i, mn := range months {
				if strings.HasPrefix(f[1], mn) {
					y := now.Year()
					if len(f) == 3 {
						y = atoi(f[2])
					}
					if y != 0 {
						return mk(y, i+1, d)
					}
				}
			}
		}
	}
	if now.IsZero() {
		return "", false
	}
	if m := dmRe.FindStringSubmatch(t); m != nil {
		return mk(now.Year(), atoi(m[2]), atoi(m[1]))
	}
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
	rel := func(days int) (string, bool) { return today.AddDate(0, 0, days).Format("2006-01-02"), true }

	words := strings.Fields(t)
	switch {
	case hasWord(words, "bugun"):
		return rel(0)
	case hasWord(words, "yarin"):
		return rel(1)
	case hasWord(words, "obur") && hasWord(words, "gun"):
		return rel(2)
	case hasWord(words, "dun"):
		return rel(-1)
	case strings.Contains(t, "ay sonu") || strings.Contains(t, "ayin sonu"):
		end := time.Date(now.Year(), now.Month()+1, 0, 0, 0, 0, 0, loc)
		return end.Format("2006-01-02"), true
	}
	for _, w := range words {
		for _, wd := range weekdays {
			if strings.HasPrefix(w, wd.name) {
				diff := (int(wd.day) - int(today.Weekday()) + 7) % 7 // bugün dahil bir sonraki
				return rel(diff)
			}
		}
	}
	return "", false
}

func hasWord(words []string, w string) bool {
	for _, x := range words {
		if strings.HasPrefix(x, w) { // "bugunku", "yarina" gibi ekler
			return true
		}
	}
	return false
}

// ParseClock "8:10", "08.10", "saat 17:45" ifadesini HH:MM'e çevirir.
func ParseClock(s string) (string, bool) {
	t := strings.TrimSpace(Fold(s))
	t = strings.TrimSpace(strings.TrimPrefix(t, "saat"))
	m := clockRe.FindStringSubmatch(t)
	if m == nil {
		return "", false
	}
	h, _ := strconv.Atoi(m[1])
	min, _ := strconv.Atoi(m[2])
	if h > 23 || min > 59 {
		return "", false
	}
	return time.Date(2000, 1, 1, h, min, 0, 0, time.UTC).Format("15:04"), true
}
