package system_apikey

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/APIParkLab/APIPark/service/subscribe"

	application_authorization "github.com/APIParkLab/APIPark/service/application-authorization"

	"github.com/APIParkLab/APIPark/service/service"

	"github.com/APIParkLab/APIPark/service/cluster"

	team_member "github.com/APIParkLab/APIPark/service/team-member"

	"github.com/eolinker/go-common/store"

	"github.com/APIParkLab/APIPark/gateway"

	"github.com/eolinker/go-common/utils"

	"github.com/google/uuid"

	system_apikey "github.com/APIParkLab/APIPark/service/system-apikey"

	system_apikey_dto "github.com/APIParkLab/APIPark/module/system-apikey/dto"
)

var _ IAPIKeyModule = new(imlAPIKeyModule)

type imlAPIKeyModule struct {
	apikeyService                   system_apikey.IAPIKeyService                    `autowired:""`
	clusterServer                   cluster.IClusterService                         `autowired:""`
	teamMemberService               team_member.ITeamMemberService                  `autowired:""`
	serviceService                  service.IServiceService                         `autowired:""`
	applicationAuthorizationService application_authorization.IAuthorizationService `autowired:""`
	subscribeService                subscribe.ISubscribeService                     `autowired:""`
	transaction                     store.ITransaction                              `autowired:""`
}

func (i *imlAPIKeyModule) MyAPIKeysByApp(ctx context.Context, appId string) ([]*system_apikey_dto.AuthorizationItem, error) {
	appInfo, err := i.serviceService.Get(ctx, appId)
	if err != nil {
		return nil, err
	}
	auths, err := i.applicationAuthorizationService.ListByApp(ctx, appId)
	if err != nil {
		return nil, err
	}
	result := make(map[string]*system_apikey_dto.AuthorizationItem)
	for _, a := range auths {
		if a.Type != "apikey" {
			continue
		}

		m := make(map[string]string)
		json.Unmarshal([]byte(a.Config), &m)
		if m["apikey"] == "" {
			continue
		}
		if _, ok := result[appInfo.Id]; !ok {
			result[appInfo.Id] = &system_apikey_dto.AuthorizationItem{
				Id:      appInfo.Id,
				Name:    appInfo.Name,
				Apikeys: []system_apikey_dto.SimpleItem{},
			}
		}
		result[appInfo.Id].Apikeys = append(result[appInfo.Id].Apikeys, system_apikey_dto.SimpleItem{
			Id:      a.UUID,
			Name:    a.Name,
			Value:   m["apikey"],
			Expired: a.ExpireTime,
		})

	}
	return utils.MapToSlice(result, func(k string, t *system_apikey_dto.AuthorizationItem) *system_apikey_dto.AuthorizationItem {
		return t
	}), nil
}

func (i *imlAPIKeyModule) MyAPIKeysByService(ctx context.Context, serviceId string) ([]*system_apikey_dto.AuthorizationItem, error) {
	list, err := i.subscribeService.ListBySubscribeStatus(ctx, serviceId, subscribe.ApplyStatusSubscribe)
	if err != nil {
		return nil, err
	}
	if len(list) < 1 {
		return nil, fmt.Errorf("no subscriber found")
	}
	appMap := utils.SliceToMapO(list, func(a *subscribe.Subscribe) (string, struct{}) {
		return a.Application, struct{}{}
	})
	members, err := i.teamMemberService.Members(ctx, nil, nil)
	if err != nil {
		return nil, err
	}
	if len(members) == 0 {
		return nil, nil
	}
	teamIds := utils.SliceToSlice(members, func(m *team_member.Member) string {
		return m.Come
	})
	apps, err := i.serviceService.AppListByTeam(ctx, teamIds...)
	if err != nil {
		return nil, err
	}
	appInfoMap := make(map[string]*service.Service)
	appIds := make([]string, 0, len(apps))
	for _, a := range apps {
		if _, ok := appMap[a.Id]; !ok {
			continue
		}
		appInfoMap[a.Id] = a
		appIds = append(appIds, a.Id)
	}

	if len(appIds) < 1 {
		return nil, fmt.Errorf("no app found")
	}
	auths, err := i.applicationAuthorizationService.ListByApp(ctx, appIds...)
	if err != nil {
		return nil, err
	}
	result := make(map[string]*system_apikey_dto.AuthorizationItem)
	for _, a := range auths {
		if a.Type != "apikey" {
			continue
		}

		appInfo, ok := appInfoMap[a.Application]
		if !ok {
			continue
		}
		m := make(map[string]string)
		json.Unmarshal([]byte(a.Config), &m)
		if m["apikey"] == "" {
			continue
		}
		if _, ok := result[appInfo.Id]; !ok {
			result[appInfo.Id] = &system_apikey_dto.AuthorizationItem{
				Id:      appInfo.Id,
				Name:    appInfo.Name,
				Apikeys: []system_apikey_dto.SimpleItem{},
			}
		}
		result[appInfo.Id].Apikeys = append(result[appInfo.Id].Apikeys, system_apikey_dto.SimpleItem{
			Id:      a.UUID,
			Name:    a.Name,
			Value:   m["apikey"],
			Expired: a.ExpireTime,
		})

	}
	return utils.MapToSlice(result, func(k string, t *system_apikey_dto.AuthorizationItem) *system_apikey_dto.AuthorizationItem {
		return t
	}), nil
}

func (i *imlAPIKeyModule) MyAPIKeys(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) {
	members, err := i.teamMemberService.Members(ctx, nil, nil)
	if err != nil {
		return nil, err
	}
	if len(members) == 0 {
		return nil, nil
	}
	teamIds := utils.SliceToSlice(members, func(m *team_member.Member) string {
		return m.Come
	})
	apps, err := i.serviceService.AppListByTeam(ctx, teamIds...)
	if err != nil {
		return nil, err
	}
	appIds := utils.SliceToSlice(apps, func(a *service.Service) string {
		return a.Id
	})
	auths, err := i.applicationAuthorizationService.ListByApp(ctx, appIds...)
	if err != nil {
		return nil, err
	}
	result := make([]*system_apikey_dto.SimpleItem, 0, len(auths))
	for _, a := range auths {
		if a.Type != "apikey" {
			continue
		}
		m := make(map[string]string)
		json.Unmarshal([]byte(a.Config), &m)
		if m["apikey"] == "" {
			continue
		}
		result = append(result, &system_apikey_dto.SimpleItem{
			Id:      a.UUID,
			Name:    a.Name,
			Value:   m["apikey"],
			Expired: a.ExpireTime,
		})

	}
	return result, nil

}

func (i *imlAPIKeyModule) Create(ctx context.Context, input *system_apikey_dto.Create) error {
	if input.Id == "" {
		input.Id = uuid.NewString()
	}
	return i.transaction.Transaction(ctx, func(ctx context.Context) error {
		err := i.apikeyService.Create(ctx, &system_apikey.Create{
			Id:      input.Id,
			Name:    input.Name,
			Value:   input.Value,
			Expired: input.Expired,
		})
		if err != nil {
			return err
		}
		client, err := i.clusterServer.GatewayClient(ctx, cluster.DefaultClusterID)
		if err != nil {
			return err
		}
		return i.online(ctx, client)
	})
}

func (i *imlAPIKeyModule) Update(ctx context.Context, id string, input *system_apikey_dto.Update) error {
	return i.transaction.Transaction(ctx, func(ctx context.Context) error {
		err := i.apikeyService.Save(ctx, id, &system_apikey.Update{
			Name:    input.Name,
			Value:   input.Value,
			Expired: input.Expired,
		})
		if err != nil {
			return err
		}
		client, err := i.clusterServer.GatewayClient(ctx, cluster.DefaultClusterID)
		if err != nil {
			return err
		}
		return i.online(ctx, client)
	})
}

func (i *imlAPIKeyModule) Delete(ctx context.Context, id string) error {
	return i.transaction.Transaction(ctx, func(ctx context.Context) error {
		err := i.apikeyService.Delete(ctx, id)
		if err != nil {
			return err
		}
		client, err := i.clusterServer.GatewayClient(ctx, cluster.DefaultClusterID)
		if err != nil {
			return err
		}
		return i.online(ctx, client)
	})
}

func (i *imlAPIKeyModule) Get(ctx context.Context, id string) (*system_apikey_dto.APIKey, error) {
	info, err := i.apikeyService.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	return system_apikey_dto.ToAPIKey(info), nil
}

func (i *imlAPIKeyModule) Search(ctx context.Context, keyword string) ([]*system_apikey_dto.Item, error) {
	list, err := i.apikeyService.Search(ctx, keyword, nil, "create_at desc")
	if err != nil {
		return nil, err
	}

	return utils.SliceToSlice(list, system_apikey_dto.ToAPIKeyItem), nil
}

func (i *imlAPIKeyModule) SimpleList(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) {
	list, err := i.apikeyService.Search(ctx, "", nil, "create_at desc")
	if err != nil {
		return nil, err
	}

	return utils.SliceToSlice(list, system_apikey_dto.ToAPIKeySimpleItem), nil
}

func (i *imlAPIKeyModule) online(ctx context.Context, client gateway.IClientDriver) error {

	// 获取所有apikey
	list, err := i.apikeyService.Search(ctx, "", nil, "create_at desc")
	if err != nil {
		return err
	}
	app := &gateway.ApplicationRelease{
		BasicItem: &gateway.BasicItem{
			ID:          "apipark-global",
			Description: "apipark global consumer",
			Version:     time.Now().Format("20060102150405"),
		},
		Authorizations: utils.SliceToSlice(list, func(a *system_apikey.APIKey) *gateway.Authorization {
			authCfg := map[string]interface{}{
				"apikey": utils.Md5(a.Value),
			}
			return &gateway.Authorization{
				Type:           "apikey",
				Position:       "header",
				TokenName:      "Authorization",
				Expire:         a.Expired,
				Config:         authCfg,
				HideCredential: true,
				Label: map[string]string{
					"authorization":      a.Id,
					"authorization_name": a.Name,
				},
			}
		}),
	}
	err = client.Application().Online(ctx, app)
	if err != nil {
		return err
	}
	return nil
}

func (i *imlAPIKeyModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
	return i.online(ctx, clientDriver)
}
