package service

import (
	"context"
	"errors"
	"key-manager/dao"
	"key-manager/key"
)

func (km *KeyManager) CreateKey(ctx context.Context, req *CreateKeyRequest) (*KeyResponse, error) {
	km.lk.Lock()
	defer km.lk.Unlock()

	if _, ok := km.mnemonics[req.Name]; !ok {
		return &KeyResponse{
			Code:    mnemonicNotExistErrCode,
			Msg:     mnemonicNotExistErrMsg,
			Network: req.Network,
			Name:    req.Name,
			Index:   req.Index,
		}, nil
	}

	account, err := createKey(km.mnemonics[req.Name], req.Network, req.Index)
	if err != nil {
		return &KeyResponse{
			Code:    createKeyErrCode,
			Msg:     err.Error(),
			Network: req.Network,
			Name:    req.Name,
			Index:   req.Index,
		}, nil
	}

	if req.Network == eth {
		err = km.dao.CreateEth(&dao.EthAddressInfo{
			Name:       req.Name,
			EthIndex:   req.Index,
			EthAddress: account.GetAddr(),
		})
	} else {
		err = km.dao.CreateTron(&dao.TronAddressInfo{
			Name:        req.Name,
			TronIndex:   req.Index,
			TronAddress: account.GetAddr(),
		})
	}

	if err != nil {
		return &KeyResponse{
			Code:    dbErrCode,
			Msg:     err.Error(),
			Network: req.Network,
			Name:    req.Name,
			Index:   req.Index,
		}, nil
	}

	km.signers[account.GetAddr()] = account.GetPriKey()

	return &KeyResponse{
		Code:    okCode,
		Msg:     okMsg,
		Network: req.Network,
		Name:    req.Name,
		Index:   req.Index,
		Address: account.GetAddr(),
	}, nil
}

func createKey(mnemonic string, network string, index int64) (key.Account, error) {
	switch network {
	case eth:
		return key.NewEthAccount(mnemonic, uint64(index))
	case tron:
		return key.NewTronAccount(mnemonic, uint64(index))
	default:
		return nil, errors.New("network must be eth or tron")
	}
}

func (km *KeyManager) GetIndex(ctx context.Context, req *GetIndexRequest) (*KeyResponse, error) {
	km.lk.Lock()
	defer km.lk.Unlock()
	addr, err := format(req.Network, req.Address)
	if err != nil {
		return &KeyResponse{
			Code:    addressErrCode,
			Msg:     addressErrMsg,
			Network: req.Network,
			Name:    "",
			Index:   -1,
		}, nil
	}

	var info dao.AccountInfo
	switch req.Network {
	case eth:
		info, err = km.dao.GetEthFromAddress(addr)
	case tron:
		info, err = km.dao.GetTronFromAddress(addr)
	default:
		return &KeyResponse{
			Code:    networkErrCode,
			Msg:     networkErrMsg,
			Network: req.Network,
			Name:    "",
			Index:   -1,
		}, nil
	}

	if err != nil {
		return &KeyResponse{
			Code:    dbErrCode,
			Msg:     err.Error(),
			Network: req.Network,
			Name:    "",
			Index:   -1,
		}, nil
	}

	return &KeyResponse{
		Code:    okCode,
		Msg:     okMsg,
		Network: req.Network,
		Name:    info.GetName(),
		Index:   info.GetIndex(),
		Address: info.GetAddr(),
	}, nil
}

func (km *KeyManager) GetAddress(ctx context.Context, req *GetAddressRequest) (*KeyResponse, error) {
	km.lk.Lock()
	defer km.lk.Unlock()

	var info dao.AccountInfo
	var err error
	switch req.Network {
	case eth:
		info, err = km.dao.GetEthFromIndex(req.Name, req.Index)
	case tron:
		info, err = km.dao.GetTronFromIndex(req.Name, req.Index)
	default:
		return &KeyResponse{
			Code:    networkErrCode,
			Msg:     networkErrMsg,
			Network: req.Network,
			Name:    "",
			Index:   -1,
		}, nil
	}

	if err != nil {
		return &KeyResponse{
			Code:    dbErrCode,
			Msg:     err.Error(),
			Network: req.Network,
			Name:    "",
			Index:   -1,
		}, nil
	}

	return &KeyResponse{
		Code:    okCode,
		Msg:     okMsg,
		Network: req.Network,
		Name:    info.GetName(),
		Index:   info.GetIndex(),
		Address: info.GetAddr(),
	}, nil
}
