package applogic

import (
	"fmt"
	"strings"

	"aijianzhan/platform/internal/agentcap"
	"aijianzhan/platform/internal/agentstore"
	"aijianzhan/platform/internal/authx"
	"aijianzhan/platform/internal/dbsync"
	"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, secret, 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
		}
	}
	if err := l.ensureAgentSyncPerm(acc); err != nil {
		return nil, err
	}
	// 重新加载 perms
	acc, err = l.svcCtx.Agents.Get(l.ctx, acc.TenantID, acc.AgentID)
	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)
	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
}

func (l *AuthLogic) ensureAgentSyncPerm(acc *agentstore.Account) error {
	if acc == nil {
		return nil
	}
	has := false
	for _, p := range acc.Perms {
		if p == authx.Perm数据同步 {
			has = true
			break
		}
	}
	if has && acc.Status == agentstore.StatusActive {
		return nil
	}
	perms := append([]string{}, acc.Perms...)
	if !has {
		perms = append(perms, authx.Perm数据同步)
	}
	st := agentstore.StatusActive
	_, err := l.svcCtx.Agents.Update(l.ctx, acc.TenantID, acc.AgentID, agentstore.UpdateInput{
		Status: &st,
		Perms:  &perms,
	})
	return err
}

// bindUserHostSync 将 host_key 智能体挂到用户公司默认同步落点并写 Binding。
func (l *AuthLogic) bindUserHostSync(u *userstore.User, hostKey, name, localID, note string) (*agentstore.Account, string, 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)
	}
	var secret string
	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, secret = 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)
	dbName := strings.TrimSpace(u.DisplayName)
	if dbName == "" {
		dbName = fmt.Sprintf("user_%d", 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:    dbName,
		DisplayName:     dbName,
		Note:            note,
	})
	return updated, secret, nil
}
