package applogic

import (
	"fmt"
	"strings"

	"aijianzhan/platform/internal/agentcap"
	"aijianzhan/platform/internal/agentstore"
	"aijianzhan/platform/internal/authx"
	"aijianzhan/platform/internal/dbsync"
	"aijianzhan/platform/internal/schema"
	"aijianzhan/platform/internal/tenantperm"
	"aijianzhan/platform/internal/types"
	"aijianzhan/platform/internal/userstore"
	"aijianzhan/platform/internal/yuhticket"
)

type YuhengTicketExchangeReq struct {
	Ticket          string `json:"ticket"`
	HostKey         string `json:"host_key"` // 须与票内一致
	LocalDatabaseID string `json:"local_database_id,omitempty"`
	Name            string `json:"name,omitempty"`
}

type YuhengTicketExchangeResp struct {
	*types.TokenResp
	OK           bool   `json:"ok"`
	ClientID     string `json:"client_id,omitempty"`
	ClientSecret string `json:"client_secret,omitempty"` // 仅新注册/轮换时返回一次
	Message      string `json:"message,omitempty"`
}

// ExchangeYuhengTicket 宇恒专属：验签凭票 → 绑定落点 → 签发智能体 JWT（免智建账号密码登录）。
func (l *AuthLogic) ExchangeYuhengTicket(req YuhengTicketExchangeReq) (*YuhengTicketExchangeResp, error) {
	cfg := l.svcCtx.Config.Agent.YuhengTicket
	if !cfg.Enabled {
		return nil, fmt.Errorf("宇恒凭票未启用（Agent.YuhengTicket.Enabled）")
	}
	if strings.TrimSpace(cfg.Secret) == "" {
		return nil, fmt.Errorf("宇恒凭票 Secret 未配置")
	}
	if l.svcCtx.Users == nil || l.svcCtx.Agents == nil || l.svcCtx.DBSync == nil {
		return nil, fmt.Errorf("bind service unavailable")
	}
	claims, err := yuhticket.Verify(req.Ticket, yuhticket.VerifyOpts{
		Secret:   cfg.Secret,
		Issuer:   cfg.Issuer,
		Audience: cfg.Audience,
	})
	if err != nil {
		return nil, err
	}
	hostKey := strings.TrimSpace(req.HostKey)
	if hostKey == "" {
		hostKey = strings.TrimSpace(claims.HostKey)
	}
	if hostKey == "" || hostKey != strings.TrimSpace(claims.HostKey) {
		return nil, fmt.Errorf("host_key 与凭票不一致")
	}
	if l.svcCtx.YuhengJTI != nil {
		if err := l.svcCtx.YuhengJTI.Consume(claims.JTI, claims.Exp); err != nil {
			return nil, err
		}
	}
	phone, err := userstore.NormalizePhone(claims.Phone)
	if err != nil {
		return nil, fmt.Errorf("凭票手机号无效")
	}
	u, err := l.svcCtx.Users.GetByPhone(l.ctx, phone)
	if err != nil || u == nil {
		return nil, fmt.Errorf("无此成员；请先在智建绑定手机或使用绑定码")
	}
	if u.TenantID <= 0 {
		return nil, fmt.Errorf("该账号尚未加入公司")
	}
	name := strings.TrimSpace(req.Name)
	if name == "" {
		name = strings.TrimSpace(claims.Name)
	}
	localID := strings.TrimSpace(req.LocalDatabaseID)
	if localID == "" {
		localID = strings.TrimSpace(claims.LocalDatabaseID)
	}
	acc, err := l.bindUserHostSync(u, hostKey, name, localID, "yuheng-ticket")
	if err != nil {
		return nil, err
	}
	// Z12h：换票时顺带自愈本租户挂死通道的 Binding/Agent
	if _, hErr := HealTenantSyncBind(l.ctx, l.svcCtx, u.TenantID); hErr == nil {
		if refreshed, gErr := l.svcCtx.Agents.Get(l.ctx, acc.TenantID, acc.AgentID); gErr == nil && refreshed != nil {
			acc = refreshed
		}
	}
	acc, err = l.ensureYuhengAgentPerms(acc)
	if err != nil {
		return nil, err
	}
	// 补权成功后才轮换密钥，禁止“绑定成功但业务接口 403”。
	secret, err := l.svcCtx.Agents.RotateSecret(l.ctx, acc.TenantID, acc.AgentID)
	if err != nil {
		return nil, fmt.Errorf("rotate client_secret: %w", 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)
	capSecret := l.svcCtx.Config.Agent.CapsuleSecret
	if capSecret == "" {
		capSecret = l.svcCtx.JWT.AccessSecret
	}
	tr := &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(capSecret, acc.TenantID, acc.AgentID),
		AgentID:     acc.AgentID,
		Permissions: append([]string{}, acc.Perms...),
		AppSlugs:    append([]string{}, acc.AppSlugs...),
	}
	fillAgentSyncOnToken(tr, acc)
	_ = l.writeBindAudit("yuheng_ticket_exchange", acc.TenantID, acc.AgentID, map[string]any{
		"phone": phone, "jti": claims.JTI, "yuheng_user_id": claims.YuhengUserID,
	})
	return &YuhengTicketExchangeResp{
		TokenResp:    tr,
		OK:           true,
		ClientID:     acc.ClientID,
		ClientSecret: secret,
		Message:      "绑定并授权完成（仅宇恒）",
	}, nil
}

var yuhengAgentPerms = []string{
	authx.Perm读取模块,
	authx.Perm写入模块,
	authx.Perm发布模块,
	authx.Perm查询数据,
	authx.Perm新增数据,
	authx.Perm导入数据,
	authx.Perm上传文件,
	authx.Perm下载文件,
	authx.Perm数据同步,
}

// ensureYuhengAgentPerms 以并集方式补齐建站权限包，保留账号已有权限。
func (l *AuthLogic) ensureYuhengAgentPerms(acc *agentstore.Account) (*agentstore.Account, error) {
	if acc == nil {
		return nil, fmt.Errorf("agent account unavailable")
	}
	perms := append([]string{}, acc.Perms...)
	seen := make(map[string]struct{}, len(perms)+len(yuhengAgentPerms))
	for _, p := range acc.Perms {
		seen[authx.NormalizePerm(p)] = struct{}{}
	}
	changed := acc.Status != agentstore.StatusActive
	for _, p := range yuhengAgentPerms {
		if _, ok := seen[p]; ok {
			continue
		}
		perms = append(perms, p)
		seen[p] = struct{}{}
		changed = true
	}
	if !changed {
		return acc, nil
	}
	perms = authx.NormalizePerms(perms)
	if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, acc.TenantID, perms); err != nil {
		return nil, fmt.Errorf("绑定授权超出公司权限额度: %w", err)
	}
	st := agentstore.StatusActive
	updated, err := l.svcCtx.Agents.Update(l.ctx, acc.TenantID, acc.AgentID, agentstore.UpdateInput{
		Status: &st,
		Perms:  &perms,
	})
	if err != nil {
		return nil, fmt.Errorf("绑定授权失败: %w", err)
	}
	return updated, nil
}

// bindUserHostSync 将 host_key 智能体挂到用户公司默认同步落点并写 Binding。
func (l *AuthLogic) bindUserHostSync(u *userstore.User, hostKey, name, localID, note string) (*agentstore.Account, error) {
	cfg := l.svcCtx.Config.DBSync
	driver := dbsync.Driver(strings.TrimSpace(cfg.DefaultRemoteDriver))
	if driver == "" {
		driver = dbsync.DriverPostgres
	}
	ch, err := l.svcCtx.DBSync.EnsureAndStartSystemDefaultChannel(dbsync.DefaultChannelOpts{
		TenantID:     u.TenantID,
		RemoteDriver: driver,
		RemoteDSN:    strings.TrimSpace(cfg.DefaultRemoteDSN),
	})
	if err != nil {
		return nil, fmt.Errorf("ensure channel: %w", err)
	}
	acc, err := l.svcCtx.Agents.FindByHostKey(l.ctx, hostKey)
	if err != nil {
		if name == "" {
			name = "离线终端 · " + maskDisplayName(u.DisplayName)
		}
		created, sec, _, regErr := l.svcCtx.Agents.Register(l.ctx, u.TenantID, name, hostKey)
		if regErr != nil {
			return nil, regErr
		}
		acc = created
		_ = sec
	} else if acc.TenantID != u.TenantID && acc.Status == agentstore.StatusPending {
		// 允许 pending 迁公司；已激活异租户拒绝
	} else if acc.TenantID != u.TenantID {
		return nil, fmt.Errorf("host_key 已绑定其它公司")
	}
	online := fmt.Sprintf("%s_u%d", dbsync.ResolveOnlineDBID("", ch.ID), u.UserID)
	// 中文昵称可接受：写入 Binding 展示字段；物理库名映射为 user_{id} / 哈希（Z35）
	display := strings.TrimSpace(u.DisplayName)
	if display == "" {
		display = fmt.Sprintf("user_%d", u.UserID)
	}
	dbName := schema.SafeSyncDatabaseName(display, acc.AgentID, u.UserID)
	updated, err := l.svcCtx.Agents.AttachSyncBind(l.ctx, acc.AgentID, u.TenantID, ch.ID, online, dbName, true)
	if err != nil {
		return nil, err
	}
	if localID == "" {
		localID = "host:" + hostKey
	}
	_, _ = l.svcCtx.DBSync.Store().EnsureBinding(dbsync.Binding{
		TenantID:        u.TenantID,
		UserID:          u.UserID,
		LocalDatabaseID: localID,
		OnlineDBID:      online,
		ChannelID:       ch.ID,
		DatabaseName:    display, // 可读名（可中文）
		DisplayName:     display,
		Note:            note,
	})
	return updated, nil
}
