package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"
)

// Discord integration for the panel — currently just the channel picker that
// Region Medic uses to choose where heal alerts post. The shared RefugeBot bot
// token lives on the controller host (default below, overridable via env) so the
// picker can enumerate the bot's channels. Posting the actual heal alerts happens
// agent-side (the medic sidecar), which has its own copy of the token.

func discordTokenPath() string {
	if v := os.Getenv("PANEL_DISCORD_TOKEN_FILE"); v != "" {
		return v
	}
	return "/home/refuge/panel/data/discord-bot-token"
}

func readDiscordToken() (string, error) {
	b, err := os.ReadFile(discordTokenPath())
	if err != nil {
		return "", err
	}
	t := strings.TrimSpace(string(b))
	if t == "" {
		return "", fmt.Errorf("discord token file is empty")
	}
	return t, nil
}

// discordGet does an authenticated GET against the Discord API and decodes JSON.
func discordGet(ctx context.Context, token, path string, out any) error {
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://discord.com/api/v10"+path, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bot "+token)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode/100 != 2 {
		return fmt.Errorf("discord %s: HTTP %d", path, resp.StatusCode)
	}
	return json.NewDecoder(resp.Body).Decode(out)
}

type discordGuild struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type discordChannelRaw struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Type     int    `json:"type"`
	Position int    `json:"position"`
}

// getDiscordChannels lists the bot's text channels across every guild it's in,
// for the Region Medic alert-channel picker. Discord channel type 0 = text,
// 5 = announcement — both can receive messages.
func (h *httpServer) getDiscordChannels(w http.ResponseWriter, r *http.Request) {
	token, err := readDiscordToken()
	if err != nil {
		writeError(w, http.StatusServiceUnavailable, "no_token", "Discord bot token not configured on the controller")
		return
	}
	ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
	defer cancel()

	var guilds []discordGuild
	if err := discordGet(ctx, token, "/users/@me/guilds", &guilds); err != nil {
		writeError(w, http.StatusBadGateway, "discord", err.Error())
		return
	}
	type chanDTO struct {
		ID        string `json:"id"`
		Name      string `json:"name"`
		GuildID   string `json:"guild_id"`
		GuildName string `json:"guild_name"`
		Label     string `json:"label"`
	}
	out := []chanDTO{}
	for _, g := range guilds {
		var chans []discordChannelRaw
		if err := discordGet(ctx, token, "/guilds/"+g.ID+"/channels", &chans); err != nil {
			continue // skip a guild whose channels we can't read
		}
		sort.Slice(chans, func(i, j int) bool { return chans[i].Position < chans[j].Position })
		for _, c := range chans {
			if c.Type != 0 && c.Type != 5 {
				continue
			}
			out = append(out, chanDTO{
				ID: c.ID, Name: c.Name, GuildID: g.ID, GuildName: g.Name,
				Label: g.Name + " / #" + c.Name,
			})
		}
	}
	writeJSON(w, http.StatusOK, map[string]any{"channels": out})
}
