package service

import (
	"bytes"
	"context"
	"fmt"
	"log/slog"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/TopherMayor/unified-media-manager/internal/db"
)

type NamingData struct {
	Title        string
	SortTitle    string
	Year         int
	Season       int
	Episode      int
	Quality      string
	Ext          string
	ReleaseGroup string
	Resolution   string
	Source       string
	Codec        string
	Artist       string
	Album        string
	Track        int
	Author       string
	Chapter      int
	Date         string
	OriginalName string
}

type NamingService struct {
	db *db.DB
}

func NewNamingService(database *db.DB) *NamingService {
	return &NamingService{db: database}
}

var DefaultTemplates = map[string]string{
	"movie":     "{{sanitize .Title}} ({{.Year}})/{{sanitize .Title}} ({{.Year}}) - {{.Quality}}.{{.Ext}}",
	"series":    "{{sanitize .Title}}/Season {{printf \"%02d\" .Season}}/{{sanitize .Title}} - S{{printf \"%02d\" .Season}}E{{printf \"%02d\" .Episode}} - {{.Quality}}.{{.Ext}}",
	"music":     "{{sanitize .Artist}}/{{sanitize .Album}}/{{printf \"%02d\" .Track}} - {{sanitize .Title}}.{{.Ext}}",
	"audiobook": "{{sanitize .Author}}/{{sanitize .Title}}/{{sanitize .Title}} - Ch{{printf \"%02d\" .Chapter}}.{{.Ext}}",
	"podcast":   "{{sanitize .Title}}/{{sanitize .Title}} - {{.Date}}.{{.Ext}}",
	"book":      "{{sanitize .Author}}/{{sanitize .Title}} ({{.Year}})/{{sanitize .Title}} ({{.Year}}).{{.Ext}}",
}

func sanitize(s string) string {
	s = strings.Map(func(r rune) rune {
		switch r {
		case '\\', '/', ':', '*', '?', '"', '<', '>', '|':
			return -1
		}
		return r
	}, s)
	for strings.Contains(s, "  ") {
		s = strings.ReplaceAll(s, "  ", " ")
	}
	return strings.TrimSpace(s)
}

func namingFuncMap() template.FuncMap {
	return template.FuncMap{
		"sanitize": sanitize,
		"lower":    strings.ToLower,
	}
}

func (s *NamingService) GetTemplate(ctx context.Context, mediaType string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	var tmpl string
	err := s.db.Pool.QueryRow(ctx,
		"SELECT template FROM naming_templates WHERE media_type = $1", mediaType).Scan(&tmpl)
	if err != nil {
		if fallback, ok := DefaultTemplates[mediaType]; ok {
			return fallback, nil
		}
		return "", fmt.Errorf("get naming template: %w", err)
	}
	return tmpl, nil
}

func (s *NamingService) Render(ctx context.Context, mediaType string, data NamingData) (string, error) {
	tmplStr, err := s.GetTemplate(ctx, mediaType)
	if err != nil {
		return "", fmt.Errorf("get template for render: %w", err)
	}

	tmpl, err := template.New("naming").Funcs(namingFuncMap()).Parse(tmplStr)
	if err != nil {
		slog.Error("failed to parse naming template", "error", err, "media_type", mediaType)
		return "", fmt.Errorf("parse naming template: %w", err)
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		slog.Error("failed to execute naming template", "error", err, "media_type", mediaType)
		return "", fmt.Errorf("execute naming template: %w", err)
	}

	return buf.String(), nil
}

func ExtractExt(filename string) string {
	ext := filepath.Ext(filename)
	if ext == "" {
		return ""
	}
	return strings.TrimPrefix(ext, ".")
}
