package store

import (
	"fmt"
	"net/http"

	"github.com/doug-martin/goqu/v9"
	"github.com/jmoiron/sqlx"

	"github.com/pingcap/test-infra/common/model"
	"github.com/pingcap/test-infra/services/tcms/service"
	"github.com/pingcap/test-infra/services/tcms/service/common"
	serror "github.com/pingcap/test-infra/services/tcms/service/common/errors"
	"github.com/pingcap/test-infra/services/tcms/utils"
	"github.com/pingcap/test-infra/services/tcms/utils/sql"
)

func (s *rmsService) init(db *sql.Database, rmsrpcSvc service.RMSRPCService, resourcePoolSvc service.ResourcePoolService) {
	s.db = db
	s.rmsrpcSvc = rmsrpcSvc
	s.resourcePoolSvc = resourcePoolSvc
}

type rmsService struct {
	db              *sql.Database
	rmsrpcSvc       service.RMSRPCService
	resourcePoolSvc service.ResourcePoolService
}

func (s *rmsService) Create(ctx utils.Context, rms model.RMS) (*model.RMS, error) {
	if s.rmsrpcSvc.Ping(rms.Endpoint) != nil {
		return nil, fmt.Errorf("unaccessable rms endpooint: %s", rms.Endpoint)
	}

	_, err := s.db.Exec("INSERT INTO rms (name, endpoint, token) VALUES (?, ?, ?)",
		rms.Name,
		rms.Endpoint,
		rms.Token,
	)
	if err != nil {
		if sql.IsErrDuplicate(err) {
			return nil, serror.NewMsg(http.StatusBadRequest, "RMS is already exist, name= %s", rms.Name)
		}
		ctx.Log().Errorf(err, "create RMS failed")
		return nil, err
	}
	return &rms, nil
}

func (s *rmsService) Delete(ctx utils.Context, name string) error {
	_, total, err := s.resourcePoolSvc.List(ctx, common.ResourcePoolQueryOpts{
		RmsName: name,
	})
	if err != nil {
		return err
	} else if total != 0 {
		return fmt.Errorf("rms %s is referred by %d resourcepools", name, total)
	}

	query, args, err := sqlx.In("DELETE FROM rms WHERE name=?", name)
	if err != nil {
		return err
	}

	_, err = s.db.Exec(query, args...)
	if err != nil {
		ctx.Log().Errorf(err, "delete rms failed")
		return err
	}
	return nil
}

func (s *rmsService) Update(ctx utils.Context, rms model.RMS) error {
	update := sql.Builder.Update("rms").Where(goqu.C("name").Eq(rms.Name)).Prepared(true)
	if rms.Endpoint != "" {
		if s.rmsrpcSvc.Ping(rms.Endpoint) != nil {
			return fmt.Errorf("unaccessable rms endpooint: %s", rms.Endpoint)
		}
		update = update.Set(goqu.Record{"endpoint": rms.Endpoint})
	}
	if rms.Token != "" {
		update = update.Set(goqu.Record{
			"token": rms.Token,
		})
	}
	updateSQL, args, _ := update.ToSQL()

	_, err := s.db.ExecContext(ctx.Request().Context(), updateSQL, args...)
	if err != nil {
		ctx.Log().Errorf(err, "update rms %s failed", rms.Name)
	}
	return nil
}

func (s *rmsService) List(ctx utils.Context) ([]*model.RMS, error) {
	var RMSs []*model.RMS

	if err := s.db.SelectContext(ctx.Request().Context(), &RMSs, `SELECT name, endpoint FROM rms`); err != nil {
		ctx.Log().Errorf(err, "select RMS failed")
		return nil, err
	}
	return RMSs, nil
}

func (s *rmsService) Get(ctx utils.Context, name string) (*model.RMS, error) {
	var rms = &model.RMS{}
	if err := s.db.GetContext(ctx.Request().Context(), rms, `SELECT * FROM rms WHERE name=?`, name); err != nil {
		if sql.IsErrNoRows(err) {
			return nil, nil
		}
		ctx.Log().Errorf(err, "get RMS failed")
		return nil, err
	}

	return rms, nil
}
