package main

import (
	"bufio"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/rs/zerolog"

	"go.mau.fi/mautrix-gmessages/libgm"
	"go.mau.fi/mautrix-gmessages/libgm/events"
	"go.mau.fi/mautrix-gmessages/libgm/gmproto"
)

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func mustReturn[T any](val T, err error) T {
	must(err)
	return val
}

var cli *libgm.Client
var log zerolog.Logger
var sess libgm.AuthData

func main() {
	log = zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
		w.Out = os.Stdout
		w.TimeFormat = time.Stamp
	})).With().Timestamp().Logger()
	file, err := os.Open("session.json")
	var doLogin bool
	if err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			panic(err)
		}
		sess = *libgm.NewAuthData()
		doLogin = true
		cookies := mustReturn(os.Open("cookies.json"))
		must(json.NewDecoder(cookies).Decode(&sess.Cookies))
	} else {
		must(json.NewDecoder(file).Decode(&sess))
		log.Info().Msg("Loaded session?")
	}
	_ = file.Close()
	cli = libgm.NewClient(&sess, log)
	cli.SetEventHandler(evtHandler)
	if doLogin {
		err = cli.DoGaiaPairing(context.TODO(), func(emoji string) {
			fmt.Println(emoji)
		})
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to pair")
		}
	} else {
		must(cli.Connect())
	}

	c := make(chan os.Signal, 1)
	input := make(chan string)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		defer close(input)
		scan := bufio.NewScanner(os.Stdin)
		for scan.Scan() {
			line := strings.TrimSpace(scan.Text())
			if len(line) > 0 {
				input <- line
			}
		}
	}()
	defer saveSession()
	for {
		select {
		case <-c:
			log.Info().Msg("Interrupt received, exiting")
			return
		case cmd := <-input:
			if len(cmd) == 0 {
				log.Info().Msg("Stdin closed, exiting")
				return
			}
			args := strings.Fields(cmd)
			cmd = args[0]
			args = args[1:]
			switch cmd {
			//case "getavatar":
			//	_, err := cli.GetFullSizeImage(args)
			//	fmt.Println(err)
			case "listcontacts":
				cli.ListContacts()
			case "topcontacts":
				cli.ListTopContacts()
			case "getconversation":
				cli.GetConversation(args[0])
			}
			//go handleCmd(strings.ToLower(cmd), args)
		}
	}
}

func saveSession() {
	file := mustReturn(os.OpenFile("session.json", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600))
	must(json.NewEncoder(file).Encode(sess))
	_ = file.Close()
}

func evtHandler(rawEvt any) {
	switch evt := rawEvt.(type) {
	case *events.ClientReady:
		log.Debug().Any("data", evt).Msg("Client is ready!")
	case *events.PairSuccessful:
		log.Debug().Any("data", evt).Msg("Pair successful")
		saveSession()
		log.Debug().Msg("Wrote session")
	case *gmproto.Message:
		log.Debug().Any("data", evt).Msg("Message event")
	case *gmproto.Conversation:
		log.Debug().Any("data", evt).Msg("Conversation event")
	case *events.BrowserActive:
		log.Debug().Any("data", evt).Msg("Browser active")
	default:
		log.Debug().Any("data", evt).Msg("Unknown event")
	}
}