package applogic

import (
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"aijianzhan/platform/internal/agentcap"
	"aijianzhan/platform/internal/agentstore"
	"aijianzhan/platform/internal/authx"
	"aijianzhan/platform/internal/types"
	"aijianzhan/platform/internal/yuhticket"
)

// YuhengRestoreByHostReq Z34b：仅凭宇恒账号 host_key 恢复已有同步绑定。
type YuhengRestoreByHostReq struct {
	Ticket  string `json:"ticket"`
	HostKey string `json:"host_key"` // 须与票内一致（= 宇恒用户 _id）
}

// YuhengRestoreByHostResp 与绑定成功对齐，便于宇恒 apply_bind_success。
type YuhengRestoreByHostResp struct {
	*types.TokenResp
	OK           bool   `json:"ok"`
	SyncBound    bool   `json:"sync_bound"`
	ClientID     string `json:"client_id,omitempty"`
	ClientSecret string `json:"client_secret,omitempty"`
	ChannelID    string `json:"channel_id,omitempty"`
	OnlineDBID   string `json:"online_db_id,omitempty"`
	TenantID     int64  `json:"tenant_id,omitempty"`
	AgentID      int64  `json:"agent_id,omitempty"`
	HostKey      string `json:"host_key,omitempty"`
	PhoneMasked  string `json:"phone_masked,omitempty"`
	Message      string `json:"message,omitempty"`
}

// RestoreBindError 映射 HTTP 状态（404 need_bind / 401 / 403）。
type RestoreBindError struct {
	HTTPStatus int
	Message    string
	NeedBind   bool
}

func (e *RestoreBindError) Error() string {
	if e == nil {
		return ""
	}
	return e.Message
}

func restoreNeedBind(msg string) error {
	return &RestoreBindError{HTTPStatus: http.StatusNotFound, Message: msg, NeedBind: true}
}

func restoreUnauthorized(msg string) error {
	return &RestoreBindError{HTTPStatus: http.StatusUnauthorized, Message: msg}
}

func restoreForbidden(msg string) error {
	return &RestoreBindError{HTTPStatus: http.StatusForbidden, Message: msg}
}

var onlineUserSuffix = regexp.MustCompile(`_u(\d+)$`)

// RestoreByYuhengHost Z34b：验签凭票 → FindByHostKey → 已 sync_bound 则轮换密钥并回传落点。
func (l *AuthLogic) RestoreByYuhengHost(req YuhengRestoreByHostReq) (*YuhengRestoreByHostResp, error) {
	cfg := l.svcCtx.Config.Agent.YuhengTicket
	if !cfg.Enabled {
		return nil, restoreUnauthorized("宇恒凭票未启用（Agent.YuhengTicket.Enabled）")
	}
	if strings.TrimSpace(cfg.Secret) == "" {
		return nil, restoreUnauthorized("宇恒凭票 Secret 未配置")
	}
	if l.svcCtx.Agents == 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,
		AllowEmptyPhone: true,
		AllowedScopes:   []string{"", "sync_bind", "sync_restore"},
	})
	if err != nil {
		msg := err.Error()
		if strings.Contains(msg, "expired") || strings.Contains(msg, "signature") ||
			strings.Contains(msg, "already used") || strings.Contains(msg, "invalid") ||
			strings.Contains(msg, "not allowed") || strings.Contains(msg, "mismatch") ||
			strings.Contains(msg, "未配置") {
			return nil, restoreUnauthorized(msg)
		}
		return nil, restoreUnauthorized(msg)
	}
	hostKey := strings.TrimSpace(req.HostKey)
	if hostKey == "" {
		hostKey = strings.TrimSpace(claims.HostKey)
	}
	if hostKey == "" || hostKey != strings.TrimSpace(claims.HostKey) {
		return nil, restoreForbidden("host_key 与凭票不一致")
	}
	if yid := strings.TrimSpace(claims.YuhengUserID); yid != "" && yid != hostKey {
		return nil, restoreForbidden("yuheng_user_id 与 host_key 不一致")
	}
	if l.svcCtx.YuhengJTI != nil {
		if err := l.svcCtx.YuhengJTI.Consume(claims.JTI, claims.Exp); err != nil {
			return nil, restoreUnauthorized(err.Error())
		}
	}

	acc, err := l.svcCtx.Agents.FindByHostKey(l.ctx, hostKey)
	if err != nil || acc == nil {
		return nil, restoreNeedBind("无此 host_key 同步绑定；请先绑定码/手机号绑定")
	}
	// 通道误删时先自愈再判断 sync_bound
	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
		}
	}
	syncBound := strings.TrimSpace(acc.ChannelID) != "" && strings.TrimSpace(acc.OnlineDBID) != ""
	if acc.Status != agentstore.StatusActive || !syncBound {
		return nil, restoreNeedBind("host_key 尚未完成同步绑定；请先绑定码/手机号绑定")
	}

	acc, err = l.ensureYuhengAgentPerms(acc)
	if err != nil {
		return nil, err
	}
	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)

	phoneMasked := maskPhoneDigits(strings.TrimSpace(claims.Phone))
	if phoneMasked == "" {
		phoneMasked = l.phoneMaskedForAgent(acc)
	}

	_ = l.writeBindAudit("yuheng_restore_by_host", acc.TenantID, acc.AgentID, map[string]any{
		"host_key": hostKey, "jti": claims.JTI, "yuheng_user_id": claims.YuhengUserID,
	})

	return &YuhengRestoreByHostResp{
		TokenResp:    tr,
		OK:           true,
		SyncBound:    true,
		ClientID:     acc.ClientID,
		ClientSecret: secret,
		ChannelID:    acc.ChannelID,
		OnlineDBID:   acc.OnlineDBID,
		TenantID:     acc.TenantID,
		AgentID:      acc.AgentID,
		HostKey:      hostKey,
		PhoneMasked:  phoneMasked,
		Message:      "已按宇恒账号恢复绑定并授权完成",
	}, nil
}

func maskPhoneDigits(phone string) string {
	phone = strings.TrimSpace(phone)
	if len(phone) < 7 {
		return ""
	}
	// 11 位国内号：135****1944
	if len(phone) == 11 {
		return phone[:3] + "****" + phone[7:]
	}
	keep := 3
	if len(phone) < keep*2 {
		return phone[:1] + "****"
	}
	return phone[:keep] + "****" + phone[len(phone)-keep:]
}

func (l *AuthLogic) phoneMaskedForAgent(acc *agentstore.Account) string {
	if acc == nil || l.svcCtx.Users == nil {
		return ""
	}
	// 优先 online_db_id 后缀 _u{userID}
	if m := onlineUserSuffix.FindStringSubmatch(strings.TrimSpace(acc.OnlineDBID)); len(m) == 2 {
		var uid int64
		_, _ = fmt.Sscanf(m[1], "%d", &uid)
		if uid > 0 {
			if u, err := l.svcCtx.Users.GetByID(l.ctx, uid); err == nil && u != nil {
				return maskPhoneDigits(u.Phone)
			}
		}
	}
	// 回落：同租户 Binding 上挂该 online_db_id 的用户
	if l.svcCtx.DBSync == nil {
		return ""
	}
	list, err := l.svcCtx.DBSync.Store().ListBindingsFiltered(acc.TenantID, 0, "")
	if err != nil {
		return ""
	}
	online := strings.TrimSpace(acc.OnlineDBID)
	for _, b := range list {
		if strings.TrimSpace(b.OnlineDBID) != online || b.UserID <= 0 {
			continue
		}
		if u, err := l.svcCtx.Users.GetByID(l.ctx, b.UserID); err == nil && u != nil {
			if m := maskPhoneDigits(u.Phone); m != "" {
				return m
			}
		}
	}
	return ""
}

// AsRestoreBindError 供 handler 取 HTTP 状态。
func AsRestoreBindError(err error) *RestoreBindError {
	var e *RestoreBindError
	if errors.As(err, &e) {
		return e
	}
	return nil
}
