package server

import (
	"fmt"
	"net"

	"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(port int) error
	Stop()
	Operation() Operation
}

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
}

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,
	}
}

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

func (hs *houstonServer) Start(port int) error {
	lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return err
	}

	if err := hs.rpcServer.Serve(lis); err != nil {
		return err
	}

	return nil
}

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

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