package notificationroute

import (
	"fmt"
	"net/http"
	"sync"
	"time"

	"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
	"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
	"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
	"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
	"bitbucket.org/nemt/nemt-portal-api/infra/config"
	"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
	"github.com/gorilla/websocket"
	"github.com/labstack/echo"
)

var (
	instance *controller
	once     sync.Once
	upgrader = websocket.Upgrader{}
)

type controller struct {
	cfg          *config.Config
	svc          *applicationservice.Service
	tnc          *tncservice.Service
	notification *notificationservice.Service
}

func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
	once.Do(func() {
		instance = &controller{
			cfg:          cfg,
			svc:          svc,
			tnc:          tnc,
			notification: notification,
		}
	})

	upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
		EnableCompression: true,
		Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
			fmt.Println("Error on websocket: ", status, " - ", reason)
		},
	}

	go svc.Notification.DeliverMessage()

	return instance
}

func (c *controller) handle(ctx echo.Context) error {
	return routeutils.ResponseAPIOK(ctx, "OK")
}

func (c *controller) handleNotification(ctx echo.Context) error {
	sRead := ctx.QueryParam("read")
	isRead := (sRead == "true")

	userUUID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "User ID is mandatory")
	if err != nil {
		return err
	}

	contactType, err := routeutils.GetAndValidateStringParam(ctx, "status", "User ID is mandatory")
	if err != nil {
		return err
	}

	n, err := c.svc.Notification.GetByUserUUIDAndReadStatus(viewmodel.User{ID: userUUID}, contactType, isRead)
	if err != nil {
		fmt.Println("Error to get notifications: ", err.Error())
	}

	return routeutils.ResponseAPIOK(ctx, n)
}

func (c *controller) handleSocket(ctx echo.Context) error {
	ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
	if err != nil {
		return err
	}
	defer ws.Close()

	userUUID := ctx.QueryParam("id")
	if userUUID == "" {
		fmt.Println("User not found")
	}

	user, err := c.svc.Users.GetByUUID(userUUID, "SP")
	if err != nil {
		fmt.Println("User not found on our system: ", err.Error())
	}

	if user.ID == "" {
		user, err = c.svc.Users.GetByUUID(userUUID, "US")
		if err != nil {
			fmt.Println("User not found on our system: ", err.Error())
		}
	}

	_, err = c.svc.Notification.Subscribe(user, ws)
	if err != nil {
		fmt.Println("Error to subscribe user: ", err.Error())
	}

	n, err := c.svc.Notification.GetByUserUUIDAndReadStatus(user, "app", false)
	if err != nil {
		fmt.Println("Error to get notifications: ", err.Error())
	}

	loc, _ := time.LoadLocation("America/Chicago")
	for _, i := range n {
		m := viewmodel.Message{
			DeliveryID:     user.ID,
			NotificationID: i.UUID,
			CreateDate:     i.Created.In(loc),
			Read:           i.Read,
			Content: viewmodel.MessageContent{
				Payload: i.Ride,
				Subject: i.Subject,
				Content: i.Message,
				Type:    i.MessageType,
			},
		}

		if err := c.svc.Notification.SendMessage(m); err != nil {
			fmt.Println("Error to send message: ", err.Error())
		}
	}

	for {
		var m viewmodel.Message
		if err := ws.ReadJSON(&m); err != nil {
			_, ok := err.(*websocket.CloseError)
			if !ok {
				fmt.Println("Error to read message: ", err.Error())
			}
		}

		if m.Content.Type == "disable-notification" {
			if err := c.svc.Notification.ReadStatus(m.NotificationID, true); err != nil {
				fmt.Println("Error to disable notification: ", err.Error())
			}
		} else {
			if err := c.svc.Notification.SendMessage(m); err != nil {
				fmt.Println("Error to send message: ", err.Error())
			}
		}
	}
}
