package server

import (
	"fmt"
	"net"

	"repositories.action2quare.com/ayo/gocommon"
	"repositories.action2quare.com/ayo/gocommon/logger"
	"repositories.action2quare.com/ayo/houston/shared"
	"repositories.action2quare.com/ayo/houston/shared/protos"

	"google.golang.org/grpc"
)

// protoc --go_out=. --go-grpc_out=. protos/*.proto
type HoustonServer interface {
	Start() error
	Stop()
	Operation() Operation
}

type serverConfig struct {
	GrpcPort         int    `json:"grpc_port"`
	StorageRoot      string `json:"storage_path"`
	MaingateApiToken string `json:"maingate_api_token"`
}

type DeployRequest struct {
	shared.DeployRequest
	hostnames []string
}

func MakeDeployRequest(req shared.DeployRequest, targets []string) DeployRequest {
	return DeployRequest{
		DeployRequest: req,
		hostnames:     targets,
	}
}

type WithdrawRequest struct {
	shared.WithdrawRequest
	hostnames []string
}

func MakeWithdrawRequest(req shared.WithdrawRequest, targets []string) WithdrawRequest {
	return WithdrawRequest{
		WithdrawRequest: req,
		hostnames:       targets,
	}
}

type StartProcessRequest struct {
	shared.StartProcessRequest
	hostnames []string
}

func MakeStartProcessRequest(req shared.StartProcessRequest, targets []string) StartProcessRequest {
	return StartProcessRequest{
		StartProcessRequest: req,
		hostnames:           targets,
	}
}

type StopProcessRequest struct {
	shared.StopProcessRequest
	hostnames []string
}

func MakeStopProcessRequest(req shared.StopProcessRequest, targets []string) StopProcessRequest {
	return StopProcessRequest{
		StopProcessRequest: req,
		hostnames:          targets,
	}
}

type RestartProcessRequest struct {
	shared.RestartProcessRequest
	hostnames []string
}

func MakeRestartRequest(req shared.RestartProcessRequest, targets []string) RestartProcessRequest {
	return RestartProcessRequest{
		RestartProcessRequest: req,
		hostnames:             targets,
	}
}

type UploadRequest struct {
	shared.UploadRequest
	hostnames []string
}

func MakeUploadRequest(req shared.UploadRequest, targets []string) UploadRequest {
	return UploadRequest{
		UploadRequest: req,
		hostnames:     targets,
	}
}

type Operation interface {
	Deploy(DeployRequest)
	Withdraw(WithdrawRequest)
	StartProcess(StartProcessRequest)
	StopProcess(StopProcessRequest)
	RestartProcess(RestartProcessRequest)
	Upload(UploadRequest)
	Hosts() map[string]hostSnapshot
	DeplyingProgress() []deployingProgress
}

type outerconfig struct {
	Houston *struct {
		Server serverConfig `json:"server"`
	} `json:"houston"`
}

func loadServerConfig() serverConfig {
	var oc outerconfig
	err := gocommon.LoadConfig[outerconfig](&oc)
	if err != nil {
		logger.Println(err)
		return serverConfig{
			GrpcPort: 8080,
		}
	}

	return oc.Houston.Server
}

func NewServer() HoustonServer {
	var opts []grpc.ServerOption
	grpcServer := grpc.NewServer(opts...)

	os := newOperationServer()
	ms := newMonitorServer()
	protos.RegisterOperationServer(grpcServer, os)
	protos.RegisterMonitorServer(grpcServer, ms)

	return &houstonServer{
		rpcServer: grpcServer,
		os:        os,
		ms:        ms,
		port:      loadServerConfig().GrpcPort,
	}
}

type houstonServer struct {
	rpcServer *grpc.Server
	os        *operationServer
	ms        *monitorServer
	port      int
}

func (hs *houstonServer) Start() error {
	logger.Println("houston server is started at port", hs.port)
	lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", hs.port))
	if err != nil {
		return err
	}

	return hs.rpcServer.Serve(lis)
}

func (hs *houstonServer) Stop() {
	hs.rpcServer.Stop()
}

func (hs *houstonServer) Operation() Operation {
	return hs.os
}
