package extension

import (
	"context"

	"git.perx.ru/perxis/perxis-go/pkg/actionurl"
	"git.perx.ru/perxis/perxis-go/pkg/errors"
	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
)

type Server struct {
	services map[string]Extension
	pb.UnimplementedExtensionServer
}

func NewServer(svc ...Extension) *Server {
	srv := &Server{
		services: make(map[string]Extension, len(svc)),
	}
	for _, s := range svc {
		srv.services[s.GetDescriptor().Extension] = s
	}
	return srv
}

func getResult(ext string, err error) *RequestResult {
	res := new(RequestResult)
	res.Extension = ext
	res.State = RequestOK

	if err != nil {
		res.State = RequestError
		res.Error = err.Error()
		errs := errors.GetErrors(err)
		if errs == nil {
			errs = append(errs, err)
		}
		for _, e := range errs {
			res.Msg += errors.GetDetail(e) + "\n"
		}
	}
	return res
}

func (srv *Server) getResults(extensions []string, fn func(svc Extension) error) []*RequestResult {

	var results []*RequestResult

	for _, e := range extensions {
		svc, ok := srv.services[e]
		if !ok {
			results = append(results, getResult(e, ErrUnknownExtension))
			continue
		}

		err := fn(svc)
		results = append(results, getResult(e, err))
	}

	return results
}

func (srv *Server) Install(ctx context.Context, request *InstallRequest) (*InstallResponse, error) {
	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Install(ctx, request) })
	return &InstallResponse{Results: res}, nil
}

func (srv *Server) Check(ctx context.Context, request *CheckRequest) (*CheckResponse, error) {
	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Check(ctx, request) })
	return &CheckResponse{Results: res}, nil
}

func (srv *Server) Uninstall(ctx context.Context, request *UninstallRequest) (*UninstallResponse, error) {
	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Uninstall(ctx, request) })
	return &UninstallResponse{Results: res}, nil
}

func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateResponse, error) {
	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Update(ctx, request) })
	return &UpdateResponse{Results: res}, nil
}

func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (*pb.ActionResponse, error) {
	ext := in.Extension
	if ext == "" {
		actionURL, err := actionurl.New(in.Action)
		if err != nil {
			return nil, err
		}
		ext = actionURL.Extension()
	}

	svc, ok := srv.services[ext]
	if !ok {
		return nil, ErrUnknownExtension
	}

	out, err := svc.Action(ctx, in)

	if out == nil {
		out = &ActionResponse{}
	}

	if err != nil {
		out.State = ResponseError
		out.Error = err.Error()
		out.Msg += errors.GetDetail(err)
	}

	return out, nil
}

func (srv *Server) Start() error {
	var errs []error
	for _, svc := range srv.services {
		if r, ok := svc.(Runnable); ok {
			if err := r.Start(); err != nil {
				errs = append(errs, err)
			}
		}
	}

	if len(errs) > 0 {
		return errors.WithErrors(ErrStart, errs...)
	}

	return nil
}

func (srv *Server) Stop() error {
	var errs []error
	for _, svc := range srv.services {
		if r, ok := svc.(Runnable); ok {
			if err := r.Stop(); err != nil {
				errs = append(errs, err)
			}
		}
	}

	if len(errs) > 0 {
		return errors.WithErrors(ErrStop, errs...)
	}

	return nil
}