package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"git.kirsle.net/apps/barertc/client/config"
	"git.kirsle.net/apps/barertc/pkg/log"
	"git.kirsle.net/apps/barertc/pkg/messages"
)

const deadlockTTL = time.Minute

/*
Deadlock detection for the chat server.

Part of the chatbot handlers. The bot will send DMs to itself on an interval
and test whether the server is responsive; if it goes down, it will issue the
/api/shutdown command to reboot the server automatically.

This function is a goroutine spawned in the background.
*/
func (h *BotHandlers) watchForDeadlock() {
	log.Info("Deadlock monitor engaged!")
	h.deadlockLastOK = time.Now()

	for {
		time.Sleep(15 * time.Second)
		go func() {
			h.client.Send(messages.Message{
				Action:  messages.ActionMessage,
				Channel: "@" + h.client.Username(),
				Message: fmt.Sprintf("deadlock ping %s", time.Now().Format(time.RFC3339)),
			})
		}()

		// Has it been a while since our last ping?
		if time.Since(h.deadlockLastOK) > deadlockTTL {
			log.Error("Deadlock detected! Rebooting the chat server!")
			h.deadlockLastOK = time.Now()
			h.rebootChatServer()
		}
	}
}

// onMessageFromSelf handles DMs sent to ourself, e.g. for deadlock detection.
func (h *BotHandlers) onMessageFromSelf(msg messages.Message) {
	// If it is our own DM channel thread, it's for deadlock detection.
	if msg.Channel == "@"+h.client.Username() {
		log.Info("(Deadlock test) got echo from self, server still seems OK: %s", msg.Message)
		h.deadlockLastOK = time.Now()
	}
}

// Reboot the chat server via web API, in case of deadlock.
func (h *BotHandlers) rebootChatServer() error {
	// API request struct for BareRTC /api/shutdown endpoint.
	var request = struct {
		APIKey string
	}{
		APIKey: config.Current.BareRTC.AdminAPIKey,
	}

	// JSON request body.
	jsonStr, err := json.Marshal(request)
	if err != nil {
		return err
	}

	// Make the API request to BareRTC.
	var url = strings.TrimSuffix(config.Current.BareRTC.URL, "/") + "/api/shutdown"
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{
		Timeout: 10 * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("RebootChatServer: error posting to BareRTC: status %d body %s", resp.StatusCode, body)
	}

	return nil
}
