package applogic

import (
	"context"
	"fmt"
	"strings"

	"aijianzhan/platform/internal/agentcap"
	"aijianzhan/platform/internal/agentstore"
	"aijianzhan/platform/internal/authx"
	"aijianzhan/platform/internal/svc"
	"aijianzhan/platform/internal/tenantperm"
	"aijianzhan/platform/internal/types"
)

type AgentAdminLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewAgentAdminLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentAdminLogic {
	return &AgentAdminLogic{ctx: ctx, svcCtx: svcCtx}
}

func (l *AgentAdminLogic) store() (agentstore.Store, error) {
	if l.svcCtx.Agents == nil {
		return nil, fmt.Errorf("agent store unavailable")
	}
	return l.svcCtx.Agents, nil
}

func (l *AgentAdminLogic) List() ([]agentstore.Account, error) {
	st, err := l.store()
	if err != nil {
		return nil, err
	}
	items, err := st.List(l.ctx, authx.TenantID(l.ctx))
	if err != nil {
		return nil, err
	}
	for i := range items {
		l.attachRole(&items[i])
	}
	return items, nil
}

func (l *AgentAdminLogic) Get(agentID int64) (*agentstore.Account, error) {
	st, err := l.store()
	if err != nil {
		return nil, err
	}
	acc, err := st.Get(l.ctx, authx.TenantID(l.ctx), agentID)
	if err != nil {
		return nil, err
	}
	l.attachRole(acc)
	return acc, nil
}

func (l *AgentAdminLogic) attachRole(acc *agentstore.Account) {
	if acc == nil || acc.RoleID <= 0 || l.svcCtx.Roles == nil {
		return
	}
	role, err := l.svcCtx.Roles.Get(l.ctx, authx.TenantID(l.ctx), acc.RoleID)
	if err != nil {
		return
	}
	acc.RoleCode = role.Code
	acc.RoleName = role.Name
}

func (l *AgentAdminLogic) resolvePerms(roleID int64, fallback []string) (int64, []string, error) {
	if roleID <= 0 {
		return 0, authx.NormalizePerms(fallback), nil
	}
	if l.svcCtx.Roles == nil {
		return 0, nil, fmt.Errorf("role store unavailable")
	}
	role, err := l.svcCtx.Roles.Get(l.ctx, authx.TenantID(l.ctx), roleID)
	if err != nil {
		return 0, nil, err
	}
	return role.RoleID, authx.NormalizePerms(append([]string{}, role.Permissions...)), nil
}

func (l *AgentAdminLogic) Create(req *types.AgentCreateReq) (*types.AgentCreateResp, error) {
	st, err := l.store()
	if err != nil {
		return nil, err
	}
	roleID, perms, err := l.resolvePerms(req.RoleID, req.Permissions)
	if err != nil {
		return nil, err
	}
	if roleID <= 0 && len(perms) == 0 {
		return nil, fmt.Errorf("role_id or permissions required")
	}
	if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), perms); err != nil {
		return nil, err
	}
	acc, secret, err := st.Create(l.ctx, authx.TenantID(l.ctx), authx.UserID(l.ctx), agentstore.CreateInput{
		Name:         req.Name,
		Perms:        perms,
		AppSlugs:     req.AppSlugs,
		Status:       agentstore.StatusActive,
		RoleID:       roleID,
		ChannelID:    req.ChannelID,
		OnlineDBID:   req.OnlineDBID,
		DatabaseName: req.DatabaseName,
	})
	if err != nil {
		return nil, err
	}
	if acc2, err := l.ensureAgentSyncBind(acc); err == nil && acc2 != nil {
		acc = acc2
	} else if err != nil {
		// 自动绑通道失败不阻断创建，但返回提示
		l.attachRole(acc)
		return &types.AgentCreateResp{Account: *acc, ClientSecret: secret}, nil
	}
	l.attachRole(acc)
	return &types.AgentCreateResp{Account: *acc, ClientSecret: secret}, nil
}

func (l *AuthLogic) SelfRegisterAgent(req *types.AgentSelfRegisterReq) (*types.AgentSelfRegisterResp, error) {
	if l.svcCtx.Agents == nil {
		return nil, fmt.Errorf("agent store unavailable")
	}
	want := l.svcCtx.Config.Agent.RegisterSecret
	if want == "" {
		want = l.svcCtx.Config.Auth.IssueSecret
	}
	if want == "" {
		want = l.svcCtx.JWT.AccessSecret
	}
	if req.RegisterSecret == "" || req.RegisterSecret != want {
		return nil, fmt.Errorf("invalid register secret")
	}
	tenantID := req.TenantID
	if tenantID <= 0 {
		tenantID = 1
	}
	acc, secret, reused, err := l.svcCtx.Agents.Register(l.ctx, tenantID, req.Name, req.HostKey)
	if err != nil {
		return nil, err
	}
	msg := "已登记为 pending，请管理员在控制台分配角色并启用后再换票"
	if reused {
		syncBound := strings.TrimSpace(acc.ChannelID) != "" && strings.TrimSpace(acc.OnlineDBID) != ""
		if acc.Status == agentstore.StatusActive && syncBound {
			msg = "已存在同步绑定账号，已轮换 client_secret；请直接用返回的 client_id/client_secret 换票，勿再走绑定表单"
		} else {
			msg = "已存在 pending 登记，已轮换 client_secret；仍须管理员分配角色并启用"
		}
	}
	return &types.AgentSelfRegisterResp{
		Account:      *acc,
		ClientSecret: secret,
		Reused:       reused,
		Message:      msg,
	}, nil
}

func (l *AgentAdminLogic) Update(agentID int64, req *types.AgentUpdateReq) (*agentstore.Account, error) {
	st, err := l.store()
	if err != nil {
		return nil, err
	}
	in := agentstore.UpdateInput{}
	if req.Name != nil {
		in.Name = req.Name
	}
	if req.Status != nil {
		in.Status = req.Status
	}
	if req.AppSlugs != nil {
		in.AppSlugs = req.AppSlugs
	}
	if req.ChannelID != nil {
		in.ChannelID = req.ChannelID
	}
	if req.OnlineDBID != nil {
		in.OnlineDBID = req.OnlineDBID
	}
	if req.DatabaseName != nil {
		in.DatabaseName = req.DatabaseName
	}
	if req.RoleID != nil {
		roleID, perms, err := l.resolvePerms(*req.RoleID, nil)
		if err != nil {
			return nil, err
		}
		if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), perms); err != nil {
			return nil, err
		}
		in.RoleID = &roleID
		in.Perms = &perms
	} else if req.Permissions != nil {
		n := authx.NormalizePerms(*req.Permissions)
		if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), n); err != nil {
			return nil, err
		}
		in.Perms = &n
	}
	acc, err := st.Update(l.ctx, authx.TenantID(l.ctx), agentID, in)
	if err != nil {
		return nil, err
	}
	// Z12c：启用为 active 且无通道时自动绑默认同步通道
	if acc.Status == agentstore.StatusActive {
		if acc2, err := l.ensureAgentSyncBind(acc); err == nil && acc2 != nil {
			acc = acc2
		}
	}
	l.attachRole(acc)
	return acc, nil
}

func (l *AgentAdminLogic) Rotate(agentID int64) (*types.AgentSecretResp, error) {
	st, err := l.store()
	if err != nil {
		return nil, err
	}
	secret, err := st.RotateSecret(l.ctx, authx.TenantID(l.ctx), agentID)
	if err != nil {
		return nil, err
	}
	acc, err := st.Get(l.ctx, authx.TenantID(l.ctx), agentID)
	if err != nil {
		return nil, err
	}
	return &types.AgentSecretResp{ClientID: acc.ClientID, ClientSecret: secret}, nil
}

func (l *AgentAdminLogic) Delete(agentID int64) error {
	st, err := l.store()
	if err != nil {
		return err
	}
	return st.Delete(l.ctx, authx.TenantID(l.ctx), agentID)
}

func (l *AuthLogic) IssueClientCredentials(clientID, clientSecret string) (*types.TokenResp, error) {
	if l.svcCtx.Agents == nil {
		return nil, fmt.Errorf("agent store unavailable")
	}
	acc, err := l.svcCtx.Agents.Authenticate(l.ctx, strings.TrimSpace(clientID), clientSecret)
	if err != nil {
		return nil, err
	}
	if l.svcCtx.DBSync != nil {
		if _, hErr := HealTenantSyncBind(l.ctx, l.svcCtx, acc.TenantID); hErr == nil {
			if refreshed, gErr := l.svcCtx.Agents.Get(l.ctx, acc.TenantID, acc.AgentID); gErr == nil && refreshed != nil {
				acc = refreshed
			}
		}
	}
	// Z38：宇恒已绑定账号用 client_credentials 换票时自动修复存量缺权。
	if strings.TrimSpace(acc.HostKey) != "" &&
		strings.TrimSpace(acc.ChannelID) != "" &&
		strings.TrimSpace(acc.OnlineDBID) != "" {
		acc, err = l.ensureYuhengAgentPerms(acc)
		if err != nil {
			return nil, err
		}
	}
	token, exp, err := authx.IssueAgentToken(l.svcCtx.JWT, acc.TenantID, acc.AgentID, acc.Perms)
	if err != nil {
		return nil, err
	}
	_ = l.svcCtx.Agents.TouchToken(l.ctx, acc.AgentID)
	secret := l.svcCtx.Config.Agent.CapsuleSecret
	if secret == "" {
		secret = l.svcCtx.JWT.AccessSecret
	}
	resp := &types.TokenResp{
		AccessToken: token,
		TokenType:   "Bearer",
		ExpiresAt:   exp,
		TenantID:    acc.TenantID,
		UserID:      acc.AgentID,
		Username:    acc.ClientID,
		DisplayName: acc.Name,
		Role:        authx.RoleAgent,
		AgentKey:    agentcap.PublicAgentKey(secret, acc.TenantID, acc.AgentID),
		AgentID:     acc.AgentID,
		Permissions: append([]string{}, acc.Perms...),
		AppSlugs:    append([]string{}, acc.AppSlugs...),
	}
	fillAgentSyncOnToken(resp, acc)
	return resp, nil
}
