package main

import (
	"context"
	"encoding/json"
	"flag"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"

	"git.esin.io/lab/weixin/pkg/mp/auth"
	"git.esin.io/lab/weixin/pkg/pubsub"
)

var (
	lisPort             string
	wxAppID             string
	wxAppSecret         string
	pgDSN               string
	messagePublisherURL string

	quitSignals = []os.Signal{
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGKILL,
		syscall.SIGTERM,
		syscall.SIGQUIT,
	}
)

func init() {
	flag.StringVar(&lisPort, "port", "13721", "server listen port")
	flag.StringVar(&wxAppID, "wx.app.id", "", "weixin mp app id")
	flag.StringVar(&wxAppSecret, "wx.app.secret", "", "weixin mp app secret")
	flag.StringVar(&pgDSN, "pg.dsn", "", "postgresql server dsn")
	flag.StringVar(&messagePublisherURL, "msg.pub.url", "", "message publisher url")
}

func main() {
	flag.Parse()
	if err := run(); err != nil {
		panic(err)
	}
}

func run() error {
	db, err := newGORMDB()
	if err != nil {
		return err
	}

	if err := autoMigrate(db); err != nil {
		return err
	}

	publisher, err := newPublihser()
	if err != nil {
		return err
	}

	srv := auth.NewServiceServer(&auth.Config{
		WeixinAppID:     wxAppID,
		WeiXinAppSecret: wxAppSecret,
		DB:              db,
		Publisher:       publisher,
	})

	s := newGRPCServer()
	auth.RegisterAuthServiceServer(s, srv)
	reflection.Register(s)

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, quitSignals...)

	lis, err := net.Listen("tcp", ":"+lisPort)
	if err != nil {
		return nil
	}

	errChan := make(chan error)
	go func() {
		errChan <- s.Serve(lis)
	}()

	select {
	case err := <-errChan:
		return err
	case <-quit:
		s.GracefulStop()
		return nil
	}
}

func newGRPCServer() *grpc.Server {
	opts := []grpc.ServerOption{}
	s := grpc.NewServer(opts...)
	return s
}

func newGORMDB() (*gorm.DB, error) {
	db, err := gorm.Open(postgres.Open(pgDSN), &gorm.Config{
		Logger: gormlogger.New(log.Default(), gormlogger.Config{
			SlowThreshold:             3 * time.Second,
			Colorful:                  true,
			IgnoreRecordNotFoundError: false,
			LogLevel:                  gormlogger.Info,
		}),
	})
	if err != nil {
		return nil, err
	}
	return db, nil
}

func autoMigrate(db *gorm.DB) error {
	models := []interface{}{
		(*auth.Token)(nil),
		(*auth.Userinfo)(nil),
		(*auth.ClientCredential)(nil),
	}
	return db.AutoMigrate(models...)
}

func newPublihser() (pubsub.Publisher, error) {
	publisher := new(mockPublisher)
	return publisher, nil
}

type mockPublisher struct{}

func (p mockPublisher) Publish(ctx context.Context, subject string, message interface{}) error {
	msg, err := json.Marshal(message)
	log.Println("publish message:", subject, string(msg))
	return err
}