package services

import (
    "encoding/json"
    "fmt"
    "os"
    "path/filepath"
    "sort"
    "strings"
    "time"
)

// NewsletterPrefs represents the normalized subset of subscriber preferences
// we care about when generating automated content.
type NewsletterPrefs struct {
    Email        string   `json:"email"`
    ContentTypes []string `json:"content_types"`        // blogs, matches, scores, events
    Competitions []string `json:"competitions"`         // FACR codes
    Frequency    string   `json:"frequency"`           // daily, weekly, matchday
}

// BuildNewsletterDigest builds an HTML digest string and subject based on cached JSON
// and subscriber preferences. It is robust to cache shape differences.
func BuildNewsletterDigest(cacheDir string, prefs NewsletterPrefs) (subject string, html string) {
    // Normalize content types
    want := make(map[string]bool)
    for _, c := range prefs.ContentTypes {
        want[strings.ToLower(strings.TrimSpace(c))] = true
    }
    if len(want) == 0 {
        // default to blogs + matches
        want["blogs"] = true
        want["matches"] = true
    }

    // Load caches (best-effort)
    art := readJSON(filepath.Join(cacheDir, "articles.json"))
    ev  := readJSON(filepath.Join(cacheDir, "events_upcoming.json"))
    facr:= readJSON(filepath.Join(cacheDir, "facr_club_info.json"))

    sections := make([]string, 0, 4)

    // Blogs/articles
    if want["blogs"] {
        items := pickArticles(art, 6)
        if len(items) > 0 {
            sections = append(sections, renderArticlesSection(items))
        }
    }

    // Upcoming events
    if want["events"] || want["matches"] {
        items := pickUpcomingEvents(ev, 6)
        if len(items) > 0 {
            sections = append(sections, renderEventsSection(items))
        }
    }

    // Matches (from FACR club info)
    if want["matches"] {
        items := pickUpcomingMatchesFromFACR(facr, prefs.Competitions, 6)
        if len(items) > 0 {
            sections = append(sections, renderMatchesSection(items))
        }
    }

    // Scores/results digest (past week) from FACR club info
    if want["scores"] {
        items := pickRecentResultsFromFACR(facr, prefs.Competitions, 8, 7*24*time.Hour)
        if len(items) > 0 {
            sections = append(sections, renderResultsSection(items))
        }
    }

    if len(sections) == 0 {
        return "Fotbal Club – přehled", "<p>Pro vybrané preference nyní nemáme novinky.</p>"
    }

    subject = "Fotbal Club – novinky a zápasy"
    html = strings.Join(sections, "\n\n")
    return subject, html
}

// Helpers

func readJSON(path string) any {
    b, err := os.ReadFile(path)
    if err != nil || len(b) == 0 { return nil }
    var v any
    _ = json.Unmarshal(b, &v)
    return v
}

type Article struct { Title, Url, Image, Excerpt string; Date time.Time }

func pickArticles(v any, n int) []Article {
    // Accept shapes: {items:[]}, {data:[]}, []
    list := asList(v)
    out := make([]Article, 0, n)
    for i, it := range list {
        if i >= n { break }
        m := asMap(it)
        a := Article{
            Title:  str(m["title"], str(m["name"], "Článek")),
            Url:    str(m["url"], urlFromSlug(m)),
            Image:  str(m["imageUrl"], str(m["image_url"], str(m["image"], ""))),
            Excerpt: str(m["excerpt"], str(m["summary"], "")),
        }
        out = append(out, a)
    }
    return out
}

type Event struct { Title, Date, Time, Url string }

func pickUpcomingEvents(v any, n int) []Event {
    list := asList(v)
    out := make([]Event, 0, n)
    for i, it := range list {
        if i >= n { break }
        m := asMap(it)
        e := Event{
            Title: str(m["title"], str(m["name"], "Událost")),
            Date:  str(m["date"], ""),
            Time:  str(m["time"], ""),
            Url:   str(m["url"], ""),
        }
        out = append(out, e)
    }
    return out
}

type Match struct { Home, Away, Date, Time, Competition, Link, Score string }

func pickUpcomingMatchesFromFACR(v any, competitions []string, n int) []Match {
    compSet := make(map[string]bool)
    for _, c := range competitions { compSet[strings.TrimSpace(strings.ToLower(c))] = true }
    now := time.Now()
    list := facrAllMatches(v)
    out := make([]Match, 0, n)
    for _, m := range list {
        ts := parseDateTimeISO(m.Date, m.Time)
        if ts.IsZero() || ts.Before(now) { continue }
        if len(compSet) > 0 {
            if !compSet[strings.ToLower(m.Competition)] { continue }
        }
        out = append(out, m)
        if len(out) >= n { break }
    }
    return out
}

func pickRecentResultsFromFACR(v any, competitions []string, n int, window time.Duration) []Match {
    compSet := make(map[string]bool)
    for _, c := range competitions { compSet[strings.TrimSpace(strings.ToLower(c))] = true }
    now := time.Now()
    from := now.Add(-window)
    list := facrAllMatches(v)
    out := make([]Match, 0, n)
    for _, m := range list {
        ts := parseDateTimeISO(m.Date, m.Time)
        if ts.IsZero() || ts.After(now) || ts.Before(from) { continue }
        // treat as result if score like "2:1" exists
        if m.Score == "" || !strings.Contains(m.Score, ":") { continue }
        if len(compSet) > 0 {
            if !compSet[strings.ToLower(m.Competition)] { continue }
        }
        out = append(out, m)
    }
    // Show latest first
    sort.Slice(out, func(i, j int) bool {
        ti := parseDateTimeISO(out[i].Date, out[i].Time)
        tj := parseDateTimeISO(out[j].Date, out[j].Time)
        return ti.After(tj)
    })
    if len(out) > n { out = out[:n] }
    return out
}

// FACR helpers (robust to various shapes)

func facrAllMatches(v any) []Match {
    out := []Match{}
    if v == nil { return out }
    m := asMap(v)
    // competitions array
    if comps, ok := m["competitions"]; ok {
        for _, c := range asList(comps) {
            cm := asMap(c)
            compName := str(cm["name"], str(cm["code"], ""))
            for _, mm := range asList(cm["matches"]) {
                out = append(out, toMatch(asMap(mm), compName))
            }
        }
    }
    // flat matches fallback
    for _, mm := range asList(m["matches"]) {
        out = append(out, toMatch(asMap(mm), ""))
    }
    return out
}

func toMatch(m map[string]any, comp string) Match {
    dt := str(m["date_time"], "")
    var date, tm string
    if dt != "" && strings.Contains(dt, " ") {
        parts := strings.SplitN(dt, " ", 2)
        date, tm = parts[0], parts[1]
    } else {
        date = str(m["date"], "")
        tm = str(m["time"], "")
    }
    return Match{
        Home:        str(m["home"], ""),
        Away:        str(m["away"], ""),
        Date:        date,
        Time:        tm,
        Competition: str(m["competition"], str(m["competition_name"], comp)),
        Link:        str(m["facr_link"], str(m["report_url"], "#")),
        Score:       str(m["score"], ""),
    }
}

func parseDateTimeISO(d, t string) time.Time {
    if d == "" { return time.Time{} }
    if t == "" { t = "00:00" }
    layout := "2006-01-02T15:04:05"
    // try shorter HH:MM format
    if len(t) == 5 { return parseTime("2006-01-02T15:04", d+"T"+t) }
    return parseTime(layout, d+"T"+t)
}

func parseTime(layout, s string) time.Time {
    if tm, err := time.Parse(layout, s); err == nil { return tm }
    // Try local
    if tm, err := time.ParseInLocation(layout, s, time.Local); err == nil { return tm }
    return time.Time{}
}

// Render helpers (inline styles for email)

func renderArticlesSection(items []Article) string {
    b := &strings.Builder{}
    fmt.Fprintf(b, "<h2 style='margin:0 0 12px 0;'>Články</h2>")
    for _, a := range items {
        fmt.Fprintf(b, "<div style='margin:10px 0;padding:10px;border-left:4px solid #2b6cb0;background:#f8fafc;'>")
        if a.Title != "" { fmt.Fprintf(b, "<div style='font-weight:600;'>%s</div>", htmlEsc(a.Title)) }
        if a.Excerpt != "" { fmt.Fprintf(b, "<div style='color:#4a5568;font-size:14px;'>%s</div>", htmlEsc(a.Excerpt)) }
        if a.Url != "" { fmt.Fprintf(b, "<div><a href='%s' style='color:#2b6cb0;'>Číst více</a></div>", a.Url) }
        fmt.Fprintf(b, "</div>")
    }
    return b.String()
}

func renderEventsSection(items []Event) string {
    b := &strings.Builder{}
    fmt.Fprintf(b, "<h2 style='margin:16px 0 12px 0;'>Události</h2>")
    for _, e := range items {
        fmt.Fprintf(b, "<div style='margin:8px 0;padding:8px;border-left:4px solid #3182ce;background:#ebf8ff;'>")
        fmt.Fprintf(b, "<div style='font-weight:600;'>%s</div>", htmlEsc(e.Title))
        if e.Date != "" || e.Time != "" {
            fmt.Fprintf(b, "<div style='color:#2c5282;font-size:14px;'>%s %s</div>", htmlEsc(e.Date), htmlEsc(e.Time))
        }
        if e.Url != "" { fmt.Fprintf(b, "<div><a href='%s' style='color:#2b6cb0;'>Zobrazit</a></div>", e.Url) }
        fmt.Fprintf(b, "</div>")
    }
    return b.String()
}

func renderMatchesSection(items []Match) string {
    b := &strings.Builder{}
    fmt.Fprintf(b, "<h2 style='margin:16px 0 12px 0;'>Nejbližší zápasy</h2>")
    for _, m := range items {
        fmt.Fprintf(b, "<div style='margin:8px 0;padding:8px;border-left:4px solid #38a169;background:#f0fff4;'>")
        fmt.Fprintf(b, "<div style='font-weight:600;'>%s vs %s</div>", htmlEsc(m.Home), htmlEsc(m.Away))
        meta := strings.TrimSpace(fmt.Sprintf("%s %s · %s", m.Date, m.Time, m.Competition))
        fmt.Fprintf(b, "<div style='color:#276749;font-size:14px;'>%s</div>", htmlEsc(meta))
        if m.Link != "" { fmt.Fprintf(b, "<div><a href='%s' style='color:#2b6cb0;'>Detail</a></div>", m.Link) }
        fmt.Fprintf(b, "</div>")
    }
    return b.String()
}

func renderResultsSection(items []Match) string {
    b := &strings.Builder{}
    fmt.Fprintf(b, "<h2 style='margin:16px 0 12px 0;'>Výsledky týdne</h2>")
    for _, m := range items {
        fmt.Fprintf(b, "<div style='margin:8px 0;padding:8px;border-left:4px solid #d69e2e;background:#fffbeb;'>")
        fmt.Fprintf(b, "<div style='font-weight:600;'>%s %s %s</div>", htmlEsc(m.Home), htmlEsc(m.Score), htmlEsc(m.Away))
        meta := strings.TrimSpace(fmt.Sprintf("%s %s · %s", m.Date, m.Time, m.Competition))
        fmt.Fprintf(b, "<div style='color:#975a16;font-size:14px;'>%s</div>", htmlEsc(meta))
        if m.Link != "" { fmt.Fprintf(b, "<div><a href='%s' style='color:#2b6cb0;'>Zápis</a></div>", m.Link) }
        fmt.Fprintf(b, "</div>")
    }
    return b.String()
}

// small utils

func asList(v any) []any {
    if v == nil { return nil }
    if arr, ok := v.([]any); ok { return arr }
    if m, ok := v.(map[string]any); ok {
        if a, ok := m["items"]; ok { return asList(a) }
        if a, ok := m["data"]; ok { return asList(a) }
    }
    return nil
}

func asMap(v any) map[string]any {
    if v == nil { return map[string]any{} }
    if m, ok := v.(map[string]any); ok { return m }
    return map[string]any{}
}

func str(v any, def string) string {
    if s, ok := v.(string); ok && s != "" { return s }
    return def
}

func urlFromSlug(m map[string]any) string {
    if s, ok := m["slug"].(string); ok && s != "" { return "/news/" + s }
    return ""
}

func htmlEsc(s string) string {
    r := strings.NewReplacer(
        "&", "&amp;",
        "<", "&lt;",
        ">", "&gt;",
        "\"", "&quot;",
        "'", "&#39;",
    )
    return r.Replace(s)
}
