package main

import (
	"fmt"
	"time"

	apns "github.com/sideshow/apns2"
	"party.at/party/cmd/party/common"
	"party.at/party/cmd/party/parlament"
)

const watcherInterval = 15 * time.Minute

func startParlWatcher(app *common.Application) {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				app.Logger.PrintError(fmt.Errorf("%s", err), nil)
			}
		}()

		ticker := time.NewTicker(watcherInterval)
		defer ticker.Stop()

		// Run once immediately so we don't wait 15 min on first boot.
		pollParlVotes(app)
		for range ticker.C {
			pollParlVotes(app)
		}
	}()
}

func pollParlVotes(app *common.Application) {
	// Use yesterday as the start date to avoid missing votes near midnight.
	dateFrom := time.Now().AddDate(0, 0, -1).Format("2006-01-02")

	docs, _, err := app.Parlament.ListDocuments(parlament.DocumentFilter{
		Chamber:  []string{"NR"},
		Period:   []string{parlament.CurrentPeriod},
		DateFrom: dateFrom,
	})
	if err != nil {
		app.Logger.PrintError(err, nil)
		return
	}

	// Keep only documents that have vote data.
	var withVotes []parlament.Document
	for _, d := range docs {
		if d.VotesFor != "" || d.VotesAgainst != "" {
			withVotes = append(withVotes, d)
		}
	}
	if len(withVotes) == 0 {
		return
	}

	numbers := make([]string, len(withVotes))
	for i, d := range withVotes {
		numbers[i] = d.Number
	}

	newNumbers, err := app.Models.ParlWatcher.FilterNew(numbers)
	if err != nil {
		app.Logger.PrintError(err, nil)
		return
	}
	if len(newNumbers) == 0 {
		return
	}

	newSet := make(map[string]bool, len(newNumbers))
	for _, n := range newNumbers {
		newSet[n] = true
	}

	var newDocs []parlament.Document
	for _, d := range withVotes {
		if newSet[d.Number] {
			newDocs = append(newDocs, d)
		}
	}

	users, err := app.Models.Users.GetAllActivated()
	if err != nil {
		app.Logger.PrintError(err, nil)
		return
	}

	for _, u := range users {
		err := app.Mailer.Send(u.Email, "parl_new_votes.tmpl", map[string]any{
			"Name":  u.Name,
			"Docs":  newDocs,
			"Count": len(newDocs),
		})
		if err != nil {
			app.Logger.PrintError(err, map[string]string{"recipient": u.Email})
		}

		if app.APNS != nil {
			sendAPNSPush(app, u.ID, len(newDocs))
		}
	}

	if err := app.Models.ParlWatcher.MarkNotified(newNumbers); err != nil {
		app.Logger.PrintError(err, nil)
		return
	}

	app.Logger.PrintInfo("parl watcher: notified new votes", map[string]string{
		"count": fmt.Sprintf("%d", len(newDocs)),
	})
}

func sendAPNSPush(app *common.Application, userID int64, count int) {
	deviceTokens, err := app.Models.DeviceTokens.GetForUser(userID)
	if err != nil {
		app.Logger.PrintError(err, nil)
		return
	}

	noun := "Abstimmung"
	if count != 1 {
		noun = "Abstimmungen"
	}
	alert := fmt.Sprintf("%d neue %s im Nationalrat", count, noun)

	for _, dt := range deviceTokens {
		notification := &apns.Notification{
			DeviceToken: dt.Token,
			Topic:       app.Config.APNS.BundleID,
			Payload:     []byte(fmt.Sprintf(`{"aps":{"alert":%q,"sound":"default"}}`, alert)),
		}
		resp, err := app.APNS.Push(notification)
		if err != nil {
			app.Logger.PrintError(err, map[string]string{"device_token": dt.Token})
			continue
		}
		if resp.StatusCode == 410 {
			// Device unregistered — clean up the stale token.
			if delErr := app.Models.DeviceTokens.Delete(dt.Token); delErr != nil {
				app.Logger.PrintError(delErr, nil)
			}
		}
	}
}
