package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	"git.thegrind.dev/thegrind/papibot/pkg/opusframes"
	dg "github.com/bwmarrin/discordgo"
	"github.com/javif89/dotenv"
)

var commandHandlers = map[string]func(s *dg.Session, i *dg.InteractionCreate){
	"play": playCommand,
}

func main() {
	env := dotenv.Load(".env")
	botToken := env.Get("DISCORD_BOT_TOKEN")
	log.Println("Starting bot")

	var discord *dg.Session
	var err error

	discord, err = dg.New(fmt.Sprintf("Bot %s", botToken))

	if err != nil {
		log.Println("Error starting the bot", err)
		return
	}

	discord.AddHandler(ready)

	discord.Identify.Intents = dg.IntentsGuilds | dg.IntentsGuildMessages | dg.IntentsGuildVoiceStates

	err = discord.Open()
	if err != nil {
		log.Println("Error opening Discord session: ", err)
	}

	// Register slash commands
	discord.AddHandler(handleSlashCommand)

	log.Println("Bot is running. Press CTRL+C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
	<-sc

	log.Println("Bot is exiting...")
	log.Println("Removing commands")

	appId := env.Get("DISCORD_APP_ID")
	cmds, _ := discord.ApplicationCommands(appId, "338782945110392832")
	for _, cmd := range cmds {
		discord.ApplicationCommandDelete(appId, "338782945110392832", cmd.ID)
	}

	discord.Close()
	log.Println("Goodbye!")
}

func playCommand(s *dg.Session, i *dg.InteractionCreate) {
	log.Println("Handling play command")
	options := i.ApplicationCommandData().Options
	url := options[0].StringValue()

	s.InteractionRespond(i.Interaction, &dg.InteractionResponse{
		Type: dg.InteractionResponseChannelMessageWithSource,
		Data: &dg.InteractionResponseData{
			Content: fmt.Sprintf("Playing dat music baybeee"),
		},
	})

	downloadVideo(url)

	// Get the channel
	msgServer, err := s.State.Guild(i.GuildID)
	if err != nil {
		log.Println("Failed to get server for action")
	}

	var voiceChannelId string
	for _, vs := range msgServer.VoiceStates {
		if vs.UserID == i.Member.User.ID {
			voiceChannelId = vs.ChannelID
		}
	}

	voiceChannel, err := s.ChannelVoiceJoin(msgServer.ID, voiceChannelId, false, true)
	if err != nil {
		log.Println("Failed to join voice channel")
	}

	log.Println("Joined channel")

	playOnVoiceChannel(voiceChannel)

	voiceChannel.Speaking(false)
	log.Println("Disconnecting from voice channel")
	time.Sleep(time.Second * 2)

	voiceChannel.Disconnect()
	log.Println("Disconnected")
}

func playOnVoiceChannel(voiceChannel *dg.VoiceConnection) {
	log.Println("Decoding")
	start := time.Now()
	frames, err := opusframes.Decode("vid.of")
	if err != nil {
		log.Println("Decoding error: ", err)
		return
	}
	duration := time.Since(start)
	log.Printf("Decoding took: %s", duration)

	// for _, f := range frames {
	// 	log.Printf("Got frame. Size: %d", len(f))
	// }

	voiceChannel.Speaking(true)
	time.Sleep(time.Second * 2)
	log.Println("Playing sound")

	for _, p := range frames {
		log.Printf("Sending packet: %d bytes", len(p))
		voiceChannel.OpusSend <- p
	}
}

func handleSlashCommand(s *dg.Session, i *dg.InteractionCreate) {
	log.Println("Command received?")
	if h, ok := commandHandlers[i.ApplicationCommandData().Name]; ok {
		h(s, i)
	}
}

func ready(s *dg.Session, event *dg.Ready) {
	commands := []*dg.ApplicationCommand{
		{
			Name:        "play",
			Description: "Play a song from youtube, spotify, apple music, etc",
			Options: []*dg.ApplicationCommandOption{
				{
					Type:        dg.ApplicationCommandOptionString,
					Name:        "url",
					Description: "URL to the song",
					Required:    true,
				},
			},
		},
	}

	env := dotenv.Load(".env")
	appId := env.Get("DISCORD_APP_ID")

	log.Println("Bot is ready")
	log.Println("Bulk registering commands")
	_, err := s.ApplicationCommandBulkOverwrite(appId, "338782945110392832", commands)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Done")
}

func downloadVideo(url string) error {
	log.Printf("Downloading: %s", url)
	os.Remove("vid.webm")
	cmd := exec.Command(
		"yt-dlp",
		"-i",
		"-q",
		"-f", "bestaudio",
		"-o",
		"vid.%(ext)s",
		url)

	err := cmd.Run()
	log.Println("Downloaded")

	start := time.Now()
	opusframes.Encode("vid.webm", "vid.of")
	duration := time.Since(start)
	log.Printf("Encoding took: %s", duration)

	return err
}