package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"src.whiteboxsystems.nl/DECOZO/okapidemo/dvzaservice/model"
	"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
)

type UIService struct {
	srv        *http.Server
	inited     bool
	data       *gorm.DB
	clientCert tls.Certificate
}

func (srv *UIService) LoadData(location string) error {
	var err error
	srv.data, err = model.GetDB(location)
	return err
}

func (srv *UIService) Addr() string {
	if srv.srv == nil {
		return ""
	}
	return srv.srv.Addr
}

func (srv *UIService) ListenAndServe() {
	if !srv.inited {
		srv.init()
	}
	log.Printf("Listening on %v\n", srv.srv.Addr)
	srv.srv.ListenAndServe()
}

func (srv *UIService) Shutdown(ctx context.Context) error {
	return srv.srv.Shutdown(ctx)
}

func (srv *UIService) init() {
	r := srv.srv.Handler.(*gin.Engine)

	r.LoadHTMLGlob("templates/*")
	r.Static("/assets", "./assets")

	r.Use(srv.Authenticate)
	r.GET("/", func(c *gin.Context) {
		c.Redirect(http.StatusMovedPermanently, "/ui")
	})
	r.GET("/ui", srv.GetIndex)
	r.GET("/ui/*page", srv.GetIndex)
	r.GET("/api/connections", srv.GetConnections)
	r.GET("/api/connections/:connID", srv.GetConnection)
	r.GET("/api/connections/:connID/:serviceID", srv.GetSubscriptions)
	r.GET("/api/connections/:connID/:serviceID/:patientID", srv.GetPatient)
	r.GET("/api/registrations", srv.GetRegistrations)
	// r.GET("/api/systems/:sysid/patients", srv.GetPatients)
	// r.GET("/api/systems/:sysid/patients/:patid", srv.GetPatient)
	srv.inited = true
}

func (srv *UIService) GetIndex(c *gin.Context) {

	c.HTML(http.StatusOK, "index.html", gin.H{"externalURL": extRpcAddr})
}

func (srv *UIService) GetConnection(c *gin.Context) {
	connID := c.Param("connID")
	connection := &sharedmodel.Connection{}
	srv.data.Where("id = ?", connID).Find(&connection)
	c.JSON(http.StatusOK, connection)
}
func (srv *UIService) GetSubscriptions(c *gin.Context) {
	connID := c.Param("connID")
	serviceID := c.Param("serviceID")
	serviceConfig := &sharedmodel.ServiceConfig{}
	srv.data.Preload("Service").Preload("Subscriptions").Where("connection_id = ? and id = ?", connID, serviceID).Find(&serviceConfig)
	c.JSON(http.StatusOK, serviceConfig)
}

func (srv *UIService) GetPatient(c *gin.Context) {
	connID := c.Param("connID")
	serviceID := c.Param("serviceID")
	patientID := c.Param("patientID")
	patient := &sharedmodel.Subscription{}
	serviceConfig := &sharedmodel.ServiceConfig{}
	srv.data.Preload("FetchProtocol").Preload("FetchProtocol.AuthConfig").Preload("Service").Where("connection_id = ? and id = ?", connID, serviceID).Find(&serviceConfig)
	srv.data.Where("service_config_id = ? and id = ?", serviceID, patientID).Find(&patient)

	protoconfig := map[string]string{}
	protometa := map[string]string{}

	err := serviceConfig.FetchProtocol.UnmarshalConfig(&protoconfig)

	if err != nil {
		c.AbortWithStatus(http.StatusBadRequest)
		return
	}

	err = patient.GetProtocolMeta(&protometa)

	if err != nil {
		c.AbortWithStatus(http.StatusBadRequest)
		return
	}

	url := fmt.Sprintf("%v?id=%v", protoconfig["url"], protometa["patientID"])

	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", serviceConfig.FetchProtocol.AuthConfig.Raw)

	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}

	resp, err := client.Do(req)

	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	if resp.StatusCode >= 400 {
		c.AbortWithError(resp.StatusCode, fmt.Errorf("%v", resp.Status))
		return
	}

	io.Copy(c.Writer, resp.Body)
}

func (srv *UIService) Authenticate(c *gin.Context) {
	// Maybe authenticate user
}

func (srv *UIService) GetConnections(c *gin.Context) {
	connections := []*sharedmodel.Connection{}
	srv.data.Preload("Services").Preload("Services.Service").Preload("Services.Subscriptions").Find(&connections)
	c.JSON(http.StatusOK, connections)
}

func (srv *UIService) GetRegistrations(c *gin.Context) {
	registrations := []*sharedmodel.Registration{}
	srv.data.Where("status = ?", sharedmodel.RegistrationStatusPending).Find(&registrations)
	c.JSON(http.StatusOK, registrations)
}

func NewUIServer(addr string) *UIService {
	cert := loadCert()
	srv := &UIService{srv: &http.Server{
		Addr:    addr,
		Handler: gin.Default(),
	}, clientCert: *cert}

	return srv
}
