package core

import (
	"context"
	"encoding/json"
	"log/slog"
	"time"

	"gitea.mrixs.me/minecraft-platform/backend/internal/database"
	"gitea.mrixs.me/minecraft-platform/backend/internal/models"
	"github.com/Tnze/go-mc/bot"
	"github.com/Tnze/go-mc/chat"
)

type pingResponse struct {
	Description chat.Message `json:"description"`
	Players     struct {
		Max    int `json:"max"`
		Online int `json:"online"`
	} `json:"players"`
	Version struct {
		Name     string `json:"name"`
		Protocol int    `json:"protocol"`
	} `json:"version"`
}

type ServerPoller struct {
	Repo *database.ServerRepository
}

func (p *ServerPoller) Start(ctx context.Context) {
	slog.Info("Starting server poller...")
	ticker := time.NewTicker(60 * time.Second)
	defer ticker.Stop()

	p.pollAllServers(ctx)

	for {
		select {
		case <-ticker.C:
			p.pollAllServers(ctx)
		case <-ctx.Done():
			slog.Info("Stopping server poller...")
			return
		}
	}
}

func (p *ServerPoller) pollAllServers(ctx context.Context) {
	servers, err := p.Repo.GetAllEnabledServers(ctx)
	if err != nil {
		slog.Error("Poller: failed to get servers", "error", err)
		return
	}

	for _, s := range servers {
		go p.pollServer(ctx, s)
	}
}

func (p *ServerPoller) pollServer(ctx context.Context, server *models.GameServer) {
	resp, delay, err := bot.PingAndList(server.Address)
	if err != nil {
		slog.Warn("Poller: failed to ping server", "server", server.Name, "address", server.Address, "error", err)
		return
	}

	var status pingResponse
	if err := json.Unmarshal(resp, &status); err != nil {
		slog.Error("Poller: failed to unmarshal status", "server", server.Name, "error", err)
		return
	}

	motdText := status.Description.String()

	updateData := &models.ServerStatus{
		StatusJSON:  string(resp),
		Motd:        motdText,
		PlayerCount: status.Players.Online,
		MaxPlayers:  status.Players.Max,
		VersionName: status.Version.Name,
		Ping:        delay.Milliseconds(),
	}

	if err := p.Repo.UpdateServerStatus(ctx, server.ID, updateData); err != nil {
		slog.Error("Poller: failed to update status", "server", server.Name, "error", err)
	} else {
		slog.Info("Poller: successfully polled server", "server", server.Name)
	}
}
