package app

import (
	"context"
	"strconv"
	"strings"

	cardObservationApp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
	"github.com/gofiber/fiber/v2"
	"go.uber.org/zap"

	"github.com/break/junhong_cmp_fiber/internal/gateway"
	"github.com/break/junhong_cmp_fiber/internal/middleware"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	assetService "github.com/break/junhong_cmp_fiber/internal/service/asset"
	customerBinding "github.com/break/junhong_cmp_fiber/internal/service/customer_binding"
	pollingSvc "github.com/break/junhong_cmp_fiber/internal/service/polling"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/logger"
	pkgMiddleware "github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/break/junhong_cmp_fiber/pkg/response"
	"github.com/go-playground/validator/v10"
)

var clientRealnameValidator = validator.New()

// ClientRealnameHandler C 端实名认证处理器
type ClientRealnameHandler struct {
	assetService          *assetService.Service
	customerBinding       *customerBinding.Service
	iotCardStore          *postgres.IotCardStore
	deviceSimBindingStore *postgres.DeviceSimBindingStore
	carrierStore          *postgres.CarrierStore
	gatewayClient         *gateway.Client
	logger                *zap.Logger
	observationSeries     cardObservationApp.BestEffortSeriesDispatcher
}

// NewClientRealnameHandler 创建 C 端实名认证处理器
func NewClientRealnameHandler(
	assetSvc *assetService.Service,
	binding *customerBinding.Service,
	iotCardStore *postgres.IotCardStore,
	deviceSimBindingStore *postgres.DeviceSimBindingStore,
	carrierStore *postgres.CarrierStore,
	gatewayClient *gateway.Client,
	logger *zap.Logger,
	_ *pollingSvc.ManualTriggerService, // 兼容旧构造签名；获取链接改由 0/3/5 观测序列收敛
) *ClientRealnameHandler {
	return &ClientRealnameHandler{
		assetService:          assetSvc,
		customerBinding:       binding,
		iotCardStore:          iotCardStore,
		deviceSimBindingStore: deviceSimBindingStore,
		carrierStore:          carrierStore,
		gatewayClient:         gatewayClient,
		logger:                logger,
	}
}

// SetObservationSeriesDispatcher 注入获取实名链接后的观测序列端口。
func (h *ClientRealnameHandler) SetObservationSeriesDispatcher(dispatcher cardObservationApp.BestEffortSeriesDispatcher) {
	h.observationSeries = dispatcher
}

// GetRealnameLink E1 获取实名认证链接
// GET /api/c/v1/realname/link
func (h *ClientRealnameHandler) GetRealnameLink(c *fiber.Ctx) error {
	// 1. 获取当前登录客户
	customerID, ok := middleware.GetCustomerID(c)
	if !ok || customerID == 0 {
		return errors.New(errors.CodeUnauthorized)
	}

	// 2. 解析请求参数
	var req dto.RealnimeLinkRequest
	if err := c.QueryParser(&req); err != nil {
		return errors.New(errors.CodeInvalidParam)
	}
	if err := clientRealnameValidator.Struct(&req); err != nil {
		logger.GetAppLogger().Warn("实名链接参数校验失败", zap.Error(err))
		return errors.New(errors.CodeInvalidParam)
	}

	ctx := c.UserContext()

	// 3. 通过标识符解析资产
	asset, err := h.assetService.Resolve(ctx, req.Identifier, false)
	if err != nil {
		return err
	}

	// 4. 验证资产归属（个人客户必须绑定过该资产）
	owned, err := h.customerBinding.OwnsAsset(ctx, customerID, asset.AssetType, asset.AssetID)
	if err != nil {
		logger.GetAppLogger().Error("查询资产归属失败",
			zap.Uint("customer_id", customerID),
			zap.Uint("asset_id", asset.AssetID),
			zap.Error(err))
		return errors.New(errors.CodeInternalError, "查询资产归属失败")
	}
	if !owned {
		return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
	}

	// 5. 定位目标卡（3 条路径）
	targetCard, err := h.resolveTargetCard(c, asset, req.ICCID)
	if err != nil {
		return err
	}

	// after_order 策略：必须先有充值或订单才能实名
	if asset.RealnamePolicy == constants.RealnamePolicyAfterOrder {
		hasValid, checkErr := h.assetService.HasValidRechargeOrPaidOrder(ctx, asset.AssetType, asset.AssetID)
		if checkErr != nil {
			logger.GetAppLogger().Warn("检查资产有效充值或订单失败", zap.Error(checkErr))
		}
		if !hasValid {
			return errors.New(errors.CodeRealnameNotAvailable)
		}
	}

	// 6. 检查实名状态
	if targetCard.RealNameStatus == constants.RealNameStatusVerified {
		return errors.New(errors.CodeInvalidStatus, "该卡已完成实名")
	}

	// 7. 获取运营商信息，根据实名链接类型生成 URL
	carrier, err := h.carrierStore.GetByID(ctx, targetCard.CarrierID)
	if err != nil {
		logger.GetAppLogger().Error("查询运营商失败",
			zap.Uint("carrier_id", targetCard.CarrierID),
			zap.Error(err))
		return errors.New(errors.CodeCarrierNotFound, "运营商信息查询失败")
	}

	resp, err := h.buildRealnameResponse(ctx, targetCard, carrier)
	if err != nil {
		return err
	}

	h.dispatchRealnameObservation(ctx, targetCard.ID)

	return response.Success(c, resp)
}

func (h *ClientRealnameHandler) dispatchRealnameObservation(ctx context.Context, cardID uint) {
	if h.observationSeries == nil {
		return
	}
	requestID := ""
	if value := pkgMiddleware.GetRequestIDFromContext(ctx); value != nil {
		requestID = *value
	}
	h.observationSeries.Dispatch(ctx, cardObservationApp.SeriesRequest{
		Scene:         constants.CardObservationSceneClientRealnameLink,
		ResourceType:  constants.CardObservationResourceTypeCard,
		ResourceID:    strconv.FormatUint(uint64(cardID), 10),
		SyncType:      constants.CardObservationSyncTypeRealname,
		ExpectedValue: "verified", Source: constants.CardObservationSourceBusinessEvent,
		RequestID: requestID, CorrelationID: requestID,
	})
}

// resolveTargetCard 根据资产类型和ICCID定位目标卡
// 支持三条路径：直接卡资产、设备+指定ICCID、设备取第一张绑定卡
func (h *ClientRealnameHandler) resolveTargetCard(c *fiber.Ctx, asset *dto.AssetResolveResponse, iccid string) (*model.IotCard, error) {
	switch {
	case asset.AssetType == "card":
		// 路径 1：资产本身就是卡，直接使用
		card, cardErr := h.iotCardStore.GetByID(c.UserContext(), asset.AssetID)
		if cardErr != nil {
			return nil, errors.New(errors.CodeIotCardNotFound, "卡信息查询失败")
		}
		return card, nil
	case asset.AssetType == "device" && iccid != "":
		// 路径 2：资产是设备，指定了 ICCID，从设备绑定中查找该卡
		return h.findCardInDeviceBindings(c, asset.AssetID, iccid)
	case asset.AssetType == "device":
		// 路径 3：资产是设备，未指定 ICCID，取第一张绑定卡（按插槽位置排序）
		return h.findFirstBoundCard(c, asset.AssetID)
	default:
		return nil, errors.New(errors.CodeInvalidParam, "不支持的资产类型")
	}
}

// buildRealnameResponse 根据运营商实名链接类型构建响应
func (h *ClientRealnameHandler) buildRealnameResponse(ctx context.Context, card *model.IotCard, carrier *model.Carrier) (*dto.RealnimeLinkResponse, error) {
	resp := &dto.RealnimeLinkResponse{
		CardInfo: dto.CardInfoBrief{
			ICCID:     card.ICCID,
			MSISDN:    card.MSISDN,
			VirtualNo: card.VirtualNo,
		},
	}
	switch carrier.RealnameLinkType {
	case constants.RealnameLinkTypeNone:
		// 该运营商不支持在线实名
		return nil, errors.New(errors.CodeInvalidStatus, "该运营商暂不支持在线实名")
	case constants.RealnameLinkTypeTemplate:
		// 模板模式：替换占位符生成实名链接
		url := carrier.RealnameLinkTemplate
		url = strings.ReplaceAll(url, "{iccid}", card.ICCID)
		url = strings.ReplaceAll(url, "{msisdn}", card.MSISDN)
		url = strings.ReplaceAll(url, "{virtual_no}", card.VirtualNo)
		resp.RealnameMode = constants.RealnameLinkTypeTemplate
		resp.RealnameURL = url
	case constants.RealnameLinkTypeGateway:
		// 网关模式：调用 Gateway 接口获取实名链接
		linkResp, gwErr := h.gatewayClient.GetRealnameLink(ctx, &gateway.CardStatusReq{
			CardNo: card.ICCID,
		})
		if gwErr != nil {
			logger.GetAppLogger().Error("Gateway 获取实名链接失败",
				zap.String("iccid", card.ICCID),
				zap.Error(gwErr))
			return nil, errors.Wrap(errors.CodeGatewayError, gwErr, "获取实名链接失败")
		}
		resp.RealnameMode = constants.RealnameLinkTypeGateway
		resp.RealnameURL = linkResp.URL
	default:
		logger.GetAppLogger().Warn("未知的实名链接类型",
			zap.Uint("carrier_id", carrier.ID),
			zap.String("realname_link_type", carrier.RealnameLinkType))
		return nil, errors.New(errors.CodeInvalidStatus, "该运营商暂不支持在线实名")
	}
	return resp, nil
}

// findCardInDeviceBindings 在设备绑定中查找指定 ICCID 的卡
func (h *ClientRealnameHandler) findCardInDeviceBindings(c *fiber.Ctx, deviceID uint, iccid string) (*model.IotCard, error) {
	ctx := c.UserContext()

	// 查询设备的所有有效绑定
	bindings, err := h.deviceSimBindingStore.ListByDeviceID(ctx, deviceID)
	if err != nil {
		logger.GetAppLogger().Error("查询设备绑定失败",
			zap.Uint("device_id", deviceID),
			zap.Error(err))
		return nil, errors.New(errors.CodeInternalError, "查询设备绑定失败")
	}

	// 收集所有绑定卡的 ID
	cardIDs := make([]uint, 0, len(bindings))
	for _, b := range bindings {
		cardIDs = append(cardIDs, b.IotCardID)
	}

	if len(cardIDs) == 0 {
		return nil, errors.New(errors.CodeIotCardNotFound, "该设备未绑定任何卡")
	}

	// 批量查询卡，匹配指定的 ICCID
	cards, err := h.iotCardStore.GetByIDs(ctx, cardIDs)
	if err != nil {
		return nil, errors.New(errors.CodeInternalError, "查询卡信息失败")
	}

	for _, card := range cards {
		if card.ICCID == iccid {
			return card, nil
		}
	}

	return nil, errors.New(errors.CodeIotCardNotFound, "该设备未绑定指定的 ICCID")
}

// findFirstBoundCard 获取设备第一张绑定卡（按插槽位置排序，取第一张）
func (h *ClientRealnameHandler) findFirstBoundCard(c *fiber.Ctx, deviceID uint) (*model.IotCard, error) {
	ctx := c.UserContext()

	// ListByDeviceID 返回 bind_status=1 的绑定，按 slot_position ASC 排序
	bindings, err := h.deviceSimBindingStore.ListByDeviceID(ctx, deviceID)
	if err != nil {
		logger.GetAppLogger().Error("查询设备绑定失败",
			zap.Uint("device_id", deviceID),
			zap.Error(err))
		return nil, errors.New(errors.CodeInternalError, "查询设备绑定失败")
	}

	if len(bindings) == 0 {
		return nil, errors.New(errors.CodeIotCardNotFound, "该设备未绑定任何卡")
	}

	// 取第一张绑定卡（插槽位置最小的）
	card, err := h.iotCardStore.GetByID(ctx, bindings[0].IotCardID)
	if err != nil {
		return nil, errors.New(errors.CodeIotCardNotFound, "卡信息查询失败")
	}

	return card, nil
}
