package main

import (
	_ "embed"
	"encoding/json"
	"fmt"
	"html/template"
	"math"
	"os"
	"time"

	"github.com/confusedpolarbear/intro_skipper_verifier/structs"
)

//go:embed report.html
var reportTemplate []byte

func compareReports(oldReportPath, newReportPath, destination string) {
	start := time.Now()

	// Populate the destination filename if none was provided
	if destination == "" {
		destination = fmt.Sprintf("report-%d.html", start.Unix())
	}

	// Open the report for writing
	f, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		panic(err)
	} else {
		defer f.Close()
	}

	fmt.Printf("Started at:    %s\n", start.Format(time.RFC1123))
	fmt.Printf("First report:  %s\n", oldReportPath)
	fmt.Printf("Second report: %s\n", newReportPath)
	fmt.Printf("Destination:   %s\n\n", destination)

	// Unmarshal both reports
	oldReport, newReport := unmarshalReport(oldReportPath), unmarshalReport(newReportPath)

	fmt.Println("[+] Comparing reports")

	// Setup a function map with helper functions to use in the template
	tmp := template.New("report")

	funcs := make(template.FuncMap)

	funcs["printTime"] = func(t time.Time) string {
		return t.Format(time.RFC1123)
	}

	funcs["printDuration"] = func(d time.Duration) string {
		return d.Round(time.Second).String()
	}

	funcs["printAnalysisSettings"] = func(pc structs.PluginConfiguration) string {
		return pc.AnalysisSettings()
	}

	funcs["printIntroductionReqs"] = func(pc structs.PluginConfiguration) string {
		return pc.IntroductionRequirements()
	}

	funcs["sortShows"] = templateSortShows
	funcs["sortSeasons"] = templateSortSeason
	funcs["compareEpisodes"] = templateCompareEpisodes
	tmp.Funcs(funcs)

	// Load the template or panic
	report := template.Must(tmp.Parse(string(reportTemplate)))

	err = report.Execute(f,
		structs.TemplateReportData{
			OldReport: oldReport,
			NewReport: newReport,
		})

	if err != nil {
		panic(err)
	}

	// Log success
	fmt.Printf("[+] Reports successfully compared in %s\n", time.Since(start).Round(time.Millisecond))
}

func unmarshalReport(path string) structs.Report {
	// Read the provided report
	contents, err := os.ReadFile(path)
	if err != nil {
		panic(err)
	}

	// Unmarshal
	var report structs.Report
	if err := json.Unmarshal(contents, &report); err != nil {
		panic(err)
	}

	// Setup maps and template data for later use
	report.Path = path
	report.Shows = make(map[string]structs.Seasons)
	report.IntroMap = make(map[string]structs.Intro)

	// Sort episodes by show and season
	for _, intro := range report.Intros {
		// Round the duration to the nearest second to avoid showing 8 decimal places in the report
		intro.Duration = float32(math.Round(float64(intro.Duration)))

		// Pretty print the intro start and end times
		intro.FormattedStart = (time.Duration(intro.IntroStart) * time.Second).String()
		intro.FormattedEnd = (time.Duration(intro.IntroEnd) * time.Second).String()

		show, season := intro.Series, intro.Season

		// If this show hasn't been seen before, allocate space for it
		if _, ok := report.Shows[show]; !ok {
			report.Shows[show] = make(structs.Seasons)
		}

		// Store this intro in the season of this show
		episodes := report.Shows[show][season]
		episodes = append(episodes, intro)
		report.Shows[show][season] = episodes

		// Store a reference to this intro in a lookup table
		report.IntroMap[intro.EpisodeId] = intro
	}

	// Print report info
	fmt.Printf("Report %s:\n", path)
	fmt.Printf("Generated with Jellyfin %s running on %s\n", report.ServerInfo.Version, report.ServerInfo.OperatingSystem)
	fmt.Printf("Analysis settings: %s\n", report.PluginConfig.AnalysisSettings())
	fmt.Printf("Introduction reqs: %s\n", report.PluginConfig.IntroductionRequirements())
	fmt.Printf("Episodes analyzed: %d\n", len(report.Intros))
	fmt.Println()

	return report
}