/*
 * Copyright (c) 2025-2026, Антон Аксенов
 * This file is part of iptvc project
 * MIT License: https://git.axenov.dev/IPTV/iptvc/src/branch/master/LICENSE
 */

package web

import (
	"axenov/iptv-checker/app/config"
	"bytes"
	"embed"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"math"
	"net/http"
	"strconv"
	"time"
)

//go:embed views/*.html
var viewsFS embed.FS

// TemplateManager управляет загрузкой и рендерингом шаблонов
type TemplateManager struct {
	templates map[string]*template.Template
	funcMap   template.FuncMap
}

// NewTemplateManager создаёт новый менеджер шаблонов
func NewTemplateManager(cfg *config.Config) (*TemplateManager, error) {
	funcMap := template.FuncMap{
		"baseUrl": func(path string) string {
			base := cfg.Site.BaseUrl
			if path == "" {
				return base
			}
			return base + "/" + path
		},
		"formatDate":   formatDate,
		"formatTimeAgo": formatTimeAgo,
		"statusClass": func(v PlaylistView) string {
			return statusClass(&v)
		},
		"statusBadge": func(v PlaylistView) string {
			return statusBadge(&v)
		},
		"jsonChannels": func(channels map[string]ChannelView) template.JS {
			arr := make([]ChannelView, 0, len(channels))
			for _, ch := range channels {
				arr = append(arr, ch)
			}
			b, _ := json.Marshal(arr)
			return template.JS(b)
		},
		"seq": func(start, end int) []int {
			result := make([]int, 0, end-start+1)
			for i := start; i <= end; i++ {
				result = append(result, i)
			}
			return result
		},
		"sub": func(a, b int) int {
			return a - b
		},
		"add": func(a, b int) int {
			return a + b
		},
		"channelCount": func(v PlaylistView) int {
			return len(v.Channels)
		},
		"groupCount": func(v PlaylistView) int {
			return len(v.Groups)
		},
		"hasTag": containsTag,
	}

	tm := &TemplateManager{
		templates: make(map[string]*template.Template),
		funcMap:   funcMap,
	}

	// parse base template
	baseContent, err := viewsFS.ReadFile("views/base.html")
	if err != nil {
		return nil, fmt.Errorf("cannot read base.html: %w", err)
	}

	pages := []string{"list", "details", "notfound"}
	for _, page := range pages {
		pageContent, err := viewsFS.ReadFile(fmt.Sprintf("views/%s.html", page))
		if err != nil {
			return nil, fmt.Errorf("cannot read %s.html: %w", page, err)
		}

		tmpl := template.New("base").Funcs(funcMap)
		tmpl, err = tmpl.Parse(string(baseContent))
		if err != nil {
			return nil, fmt.Errorf("cannot parse base template: %w", err)
		}
		tmpl, err = tmpl.Parse(string(pageContent))
		if err != nil {
			return nil, fmt.Errorf("cannot parse %s template: %w", page, err)
		}

		tm.templates[page] = tmpl
	}

	return tm, nil
}

// Render рендерит шаблон страницы
func (tm *TemplateManager) Render(w io.Writer, page string, data *PageData) {
	tmpl, ok := tm.templates[page]
	if !ok {
		http.Error(w.(http.ResponseWriter), "Template not found", http.StatusInternalServerError)
		return
	}

	var buf bytes.Buffer
	if err := tmpl.ExecuteTemplate(&buf, "base", data); err != nil {
		log.Printf("Template error (%s): %s", page, err)
		http.Error(w.(http.ResponseWriter), "Template error", http.StatusInternalServerError)
		return
	}

	w.Write(buf.Bytes())
}

// formatTimeAgo возвращает относительное время на русском
func formatTimeAgo(ts int64) string {
	if ts == 0 {
		return "неизвестно"
	}
	diff := time.Now().Unix() - ts
	if diff < 0 {
		diff = 0
	}
	if diff < 60 {
		return "только что"
	}
	if diff < 3600 {
		minutes := diff / 60
		return strconv.FormatInt(minutes, 10) + " " + getNounForm(int(minutes), "минуту", "минуты", "минут") + " назад"
	}
	if diff < 86400 {
		hours := diff / 3600
		return strconv.FormatInt(hours, 10) + " " + getNounForm(int(hours), "час", "часа", "часов") + " назад"
	}
	if diff < 604800 {
		days := diff / 86400
		return strconv.FormatInt(days, 10) + " " + getNounForm(int(days), "день", "дня", "дней") + " назад"
	}
	return formatDate(ts)
}

// getNounForm возвращает правильную форму существительного для числительного
func getNounForm(number int, form1, form2, form5 string) string {
	abs := int(math.Abs(float64(number)))
	lastDigit := abs % 10
	lastTwoDigits := abs % 100

	if lastTwoDigits >= 11 && lastTwoDigits <= 14 {
		return form5
	}
	if lastDigit == 1 {
		return form1
	}
	if lastDigit >= 2 && lastDigit <= 4 {
		return form2
	}
	return form5
}
