package service

import (
	"context"
	"encoding/json"
	"kra/internal/biz/system"
	"kra/internal/service/dto"
	"time"
)

type VersionService struct{ uc *system.VersionUsecase }

func NewVersionService(uc *system.VersionUsecase) *VersionService { return &VersionService{uc: uc} }

type versionExportInfo struct {
	Name        string `json:"name"`
	Code        string `json:"code"`
	Description string `json:"description"`
	ExportTime  string `json:"exportTime"`
}

type versionExportMenuParameter struct {
	ID            uint      `json:"ID"`
	CreatedAt     time.Time `json:"CreatedAt"`
	UpdatedAt     time.Time `json:"UpdatedAt"`
	SysBaseMenuID uint      `json:"SysBaseMenuID"`
	Type          string    `json:"type"`
	Key           string    `json:"key"`
	Value         string    `json:"value"`
}

type versionExportMenuButton struct {
	ID            uint      `json:"ID"`
	CreatedAt     time.Time `json:"CreatedAt"`
	UpdatedAt     time.Time `json:"UpdatedAt"`
	Name          string    `json:"name"`
	Description   string    `json:"desc"`
	SysBaseMenuID uint      `json:"sysBaseMenuID"`
}

type versionExportMenu struct {
	ID          uint                         `json:"ID"`
	CreatedAt   time.Time                    `json:"CreatedAt"`
	UpdatedAt   time.Time                    `json:"UpdatedAt"`
	ParentID    uint                         `json:"parentId"`
	Path        string                       `json:"path"`
	Name        string                       `json:"name"`
	Hidden      bool                         `json:"hidden"`
	Component   string                       `json:"component"`
	Sort        int                          `json:"sort"`
	Meta        dto.VersionMenuMetaRequest   `json:"meta"`
	Authorities []any                        `json:"authoritys"`
	Children    []*versionExportMenu         `json:"children"`
	Parameters  []versionExportMenuParameter `json:"parameters"`
	MenuButtons []versionExportMenuButton    `json:"menuBtn"`
}

type versionExportAPI struct {
	ID          uint      `json:"ID"`
	CreatedAt   time.Time `json:"CreatedAt"`
	UpdatedAt   time.Time `json:"UpdatedAt"`
	Path        string    `json:"path"`
	Description string    `json:"description"`
	APIGroup    string    `json:"apiGroup"`
	Method      string    `json:"method"`
}

type versionExportDictionaryDetail struct {
	ID           uint                            `json:"ID"`
	CreatedAt    time.Time                       `json:"CreatedAt"`
	UpdatedAt    time.Time                       `json:"UpdatedAt"`
	Label        string                          `json:"label"`
	Value        string                          `json:"value"`
	Extend       string                          `json:"extend"`
	Status       *bool                           `json:"status"`
	Sort         int                             `json:"sort"`
	DictionaryID int                             `json:"sysDictionaryID"`
	ParentID     *uint                           `json:"parentID"`
	Children     []versionExportDictionaryDetail `json:"children"`
	Level        int                             `json:"level"`
	Path         string                          `json:"path"`
	Disabled     bool                            `json:"disabled"`
}

type versionExportDictionary struct {
	ID        uint                            `json:"ID"`
	CreatedAt time.Time                       `json:"CreatedAt"`
	UpdatedAt time.Time                       `json:"UpdatedAt"`
	Name      string                          `json:"name"`
	Type      string                          `json:"type"`
	Status    *bool                           `json:"status"`
	Desc      string                          `json:"desc"`
	ParentID  *uint                           `json:"parentID"`
	Children  []versionExportDictionary       `json:"children"`
	Details   []versionExportDictionaryDetail `json:"sysDictionaryDetails"`
}

type versionExportDocument struct {
	Version      versionExportInfo         `json:"version"`
	Menus        []*versionExportMenu      `json:"menus"`
	APIs         []versionExportAPI        `json:"apis"`
	Dictionaries []versionExportDictionary `json:"dictionaries"`
}

func exportMenu(value *system.Menu) *versionExportMenu {
	menu := &versionExportMenu{
		Path: value.Path, Name: value.Name, Hidden: value.Hidden, Component: value.Component, Sort: value.Sort,
		Meta: dto.VersionMenuMetaRequest{ActiveName: value.ActiveName, KeepAlive: value.KeepAlive, DefaultMenu: value.DefaultMenu, Title: value.Title, Icon: value.Icon, CloseTab: value.CloseTab, TransitionType: value.TransitionType},
	}
	if len(value.Parameters) > 0 {
		menu.Parameters = make([]versionExportMenuParameter, 0, len(value.Parameters))
		for _, parameter := range value.Parameters {
			menu.Parameters = append(menu.Parameters, versionExportMenuParameter{Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
		}
	}
	if len(value.Buttons) > 0 {
		menu.MenuButtons = make([]versionExportMenuButton, 0, len(value.Buttons))
		for _, button := range value.Buttons {
			menu.MenuButtons = append(menu.MenuButtons, versionExportMenuButton{Name: button.Name, Description: button.Description})
		}
	}
	if len(value.Children) > 0 {
		menu.Children = make([]*versionExportMenu, 0, len(value.Children))
		for _, child := range value.Children {
			menu.Children = append(menu.Children, exportMenu(child))
		}
	}
	return menu
}

func versionDTO(v *system.Version) *dto.VersionResponse {
	return &dto.VersionResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, VersionName: v.Name, VersionCode: v.Code, Description: v.Description, VersionData: v.Data}
}

func versionValue(value *string) string {
	if value == nil {
		return ""
	}
	return *value
}

func versionPointer(value string) *string { return &value }
func (s *VersionService) Versions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]*dto.VersionResponse, int64, error) {
	items, total, err := s.uc.ListVersions(ctx, page, size, name, code, createdAtRange)
	if err != nil {
		return nil, 0, err
	}
	out := make([]*dto.VersionResponse, 0, len(items))
	for _, v := range items {
		out = append(out, versionDTO(v))
	}
	return out, total, nil
}
func (s *VersionService) Version(ctx context.Context, id uint) (*dto.VersionResponse, error) {
	v, err := s.uc.FindVersion(ctx, id)
	if err != nil {
		return nil, err
	}
	return versionDTO(v), nil
}
func (s *VersionService) VersionData(ctx context.Context, id uint) (string, string, error) {
	v, err := s.uc.FindVersion(ctx, id)
	if err != nil {
		return "", "", err
	}
	if v.Data != nil && *v.Data != "" {
		return *v.Data, versionValue(v.Code), nil
	}
	payload := versionExportDocument{
		Version: versionExportInfo{Name: versionValue(v.Name), Code: versionValue(v.Code), Description: versionValue(v.Description), ExportTime: v.CreatedAt.Format("2006-01-02 15:04:05")},
		Menus:   []*versionExportMenu{},
		APIs:    []versionExportAPI{},
	}
	raw, marshalErr := json.MarshalIndent(payload, "", "  ")
	if marshalErr != nil {
		return "", "", marshalErr
	}
	return string(raw), versionValue(v.Code), nil
}
func (s *VersionService) DeleteVersions(ctx context.Context, ids []uint) error {
	return s.uc.DeleteVersions(ctx, ids)
}

func (s *VersionService) Export(ctx context.Context, name, code, description string, menuIDs, apiIDs, dictIDs []uint) error {
	bundle, err := s.uc.BuildVersionBundle(ctx, menuIDs, apiIDs, dictIDs)
	if err != nil {
		return err
	}
	bundle.Name = name
	bundle.Code = code
	bundle.Description = description
	bundle.ExportTime = time.Now().Format("2006-01-02 15:04:05")
	var menus []*versionExportMenu
	if bundle.Menus != nil {
		menus = make([]*versionExportMenu, 0, len(bundle.Menus))
		for _, value := range bundle.Menus {
			menus = append(menus, exportMenu(value))
		}
	}
	apis := make([]versionExportAPI, 0, len(bundle.APIs))
	for _, value := range bundle.APIs {
		apis = append(apis, versionExportAPI{Path: value.Path, Description: value.Description, APIGroup: value.APIGroup, Method: value.Method})
	}
	dictionaries := make([]versionExportDictionary, 0, len(bundle.Dictionaries))
	for _, value := range bundle.Dictionaries {
		details := make([]versionExportDictionaryDetail, 0, len(value.Details))
		for _, detail := range value.Details {
			details = append(details, versionExportDictionaryDetail{Label: detail.Label, Value: detail.Value, Extend: detail.Extend, Status: detail.Status, Sort: detail.Sort})
		}
		dictionaries = append(dictionaries, versionExportDictionary{Name: value.Name, Type: value.Type, Status: value.Status, Desc: value.Desc, Details: details})
	}
	payload := versionExportDocument{Version: versionExportInfo{Name: name, Code: code, Description: description, ExportTime: bundle.ExportTime}, Menus: menus, APIs: apis, Dictionaries: dictionaries}
	raw, err := json.MarshalIndent(payload, "", "  ")
	if err != nil {
		return &system.VersionStageError{Stage: system.VersionStageJSON, Err: err}
	}
	if err = s.uc.CreateVersion(ctx, &system.Version{Name: versionPointer(name), Code: versionPointer(code), Description: versionPointer(description), Data: versionPointer(string(raw))}); err != nil {
		return &system.VersionStageError{Stage: system.VersionStageSave, Err: err}
	}
	return nil
}
func (s *VersionService) Import(ctx context.Context, bundle *system.VersionBundle) error {
	return s.uc.ImportVersionBundle(ctx, bundle)
}
func versionMenuDomain(value dto.VersionMenuRequest) *system.Menu {
	menu := &system.Menu{Path: value.Path, Name: value.Name, Hidden: value.Hidden, Component: value.Component, Sort: value.Sort, ActiveName: value.Meta.ActiveName, KeepAlive: value.Meta.KeepAlive, DefaultMenu: value.Meta.DefaultMenu, Title: value.Meta.Title, Icon: value.Meta.Icon, CloseTab: value.Meta.CloseTab, TransitionType: value.Meta.TransitionType}
	for _, button := range value.MenuButtons {
		menu.Buttons = append(menu.Buttons, &system.MenuButton{Name: button.Name, Description: button.Description})
	}
	for _, parameter := range value.Parameters {
		menu.Parameters = append(menu.Parameters, &system.MenuParameter{Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
	}
	for _, child := range value.Children {
		menu.Children = append(menu.Children, versionMenuDomain(child))
	}
	return menu
}
func (s *VersionService) ImportRequest(ctx context.Context, req *dto.ImportVersionRequest) error {
	if req.Version.Name == "" || req.Version.Code == "" {
		return system.ErrInvalidVersion
	}
	bundle := &system.VersionBundle{Name: req.Version.Name, Code: req.Version.Code, Description: req.Version.Description, ExportTime: req.Version.ExportTime}
	for _, menu := range req.Menus {
		bundle.Menus = append(bundle.Menus, versionMenuDomain(menu))
	}
	for _, api := range req.APIs {
		bundle.APIs = append(bundle.APIs, &system.API{Path: api.Path, Description: api.Description, APIGroup: api.APIGroup, Method: api.Method})
	}
	for _, dictionary := range req.Dictionaries {
		item := &system.Dictionary{Name: dictionary.Name, Type: dictionary.Type, Status: dictionary.Status, Desc: dictionary.Description}
		for _, detail := range dictionary.Details {
			item.Details = append(item.Details, &system.DictionaryDetail{Label: detail.Label, Value: detail.Value, Extend: detail.Extend, Status: detail.Status, Sort: detail.Sort, ParentID: detail.ParentID, Level: detail.Level, Path: detail.Path})
		}
		bundle.Dictionaries = append(bundle.Dictionaries, item)
	}
	if err := s.Import(ctx, bundle); err != nil {
		return err
	}
	raw, _ := json.Marshal(req)
	_ = s.uc.CreateVersion(ctx, &system.Version{
		Name:        versionPointer(req.Version.Name),
		Code:        versionPointer(req.Version.Code + "_imported_" + time.Now().Format("20060102150405")),
		Description: versionPointer("导入版本: " + req.Version.Description),
		Data:        versionPointer(string(raw)),
	})
	return nil
}
