package cardobservation

import (
	"context"
	"strconv"
	"strings"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"

	cardapp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
)

// BestEffortSeriesDispatcher 将读入口触发失败降级为中文安全日志。
type BestEffortSeriesDispatcher struct {
	trigger               *cardapp.SeriesTrigger
	logger                *zap.Logger
	jobs                  chan cardapp.SeriesRequest
	deviceJobs            chan cardapp.DeviceCardsSeriesRequest
	deviceControlJobs     chan cardapp.DeviceControlSeriesRequest
	realnameJobs          chan cardapp.RealnameCapabilitySeriesRequest
	deviceSimBindingStore *postgres.DeviceSimBindingStore
	carrierStore          *postgres.CarrierStore
}

const bestEffortSeriesQueueSize = 1024

// NewBestEffortSeriesDispatcher 创建读入口观测序列分发器。
func NewBestEffortSeriesDispatcher(trigger *cardapp.SeriesTrigger, logger *zap.Logger, deviceSimBindingStore *postgres.DeviceSimBindingStore, carrierStore *postgres.CarrierStore) *BestEffortSeriesDispatcher {
	dispatcher := &BestEffortSeriesDispatcher{
		trigger: trigger, logger: logger,
		jobs:                  make(chan cardapp.SeriesRequest, bestEffortSeriesQueueSize),
		deviceJobs:            make(chan cardapp.DeviceCardsSeriesRequest, bestEffortSeriesQueueSize),
		deviceControlJobs:     make(chan cardapp.DeviceControlSeriesRequest, bestEffortSeriesQueueSize),
		realnameJobs:          make(chan cardapp.RealnameCapabilitySeriesRequest, bestEffortSeriesQueueSize),
		deviceSimBindingStore: deviceSimBindingStore, carrierStore: carrierStore,
	}
	go dispatcher.run()
	return dispatcher
}

// DispatchDeviceControl 在后台解析设备控制前后的相关卡，避免增加原操作响应耗时。
func (d *BestEffortSeriesDispatcher) DispatchDeviceControl(_ context.Context, request cardapp.DeviceControlSeriesRequest) {
	if d == nil || d.trigger == nil || request.DeviceID == 0 {
		return
	}
	request.Request = normalizeRequest(request.Request)
	select {
	case d.deviceControlJobs <- request:
	default:
		d.logFailure(request.Request, "", false, nil, "后台设备控制观测队列已满")
	}
}

// Dispatch 尝试创建或合并序列，失败不向原查询调用方返回。
func (d *BestEffortSeriesDispatcher) Dispatch(_ context.Context, request cardapp.SeriesRequest) {
	if d == nil || d.trigger == nil {
		return
	}
	request = normalizeRequest(request)
	select {
	case d.jobs <- request:
	default:
		d.logFailure(request, "", false, nil, "后台观测队列已满")
	}
}

// DispatchDeviceCards 在后台展开设备当前有效绑定卡，避免请求协程逐卡查询数据库。
func (d *BestEffortSeriesDispatcher) DispatchDeviceCards(_ context.Context, request cardapp.DeviceCardsSeriesRequest) {
	if d == nil || d.trigger == nil || request.DeviceID == 0 || d.deviceSimBindingStore == nil {
		return
	}
	request.Request = normalizeRequest(request.Request)
	select {
	case d.deviceJobs <- request:
	default:
		d.logFailure(request.Request, "", false, nil, "后台设备观测队列已满")
	}
}

// DispatchRealnameWithCapability 在后台读取运营商实名能力，none 能力不触发观测。
func (d *BestEffortSeriesDispatcher) DispatchRealnameWithCapability(_ context.Context, request cardapp.RealnameCapabilitySeriesRequest) {
	if d == nil || d.trigger == nil || request.CarrierID == 0 || d.carrierStore == nil {
		return
	}
	request.Request = normalizeRequest(request.Request)
	select {
	case d.realnameJobs <- request:
	default:
		d.logFailure(request.Request, "", false, nil, "后台实名观测队列已满")
	}
}

func (d *BestEffortSeriesDispatcher) run() {
	for {
		select {
		case request := <-d.jobs:
			d.triggerRequest(request)
		case request := <-d.deviceJobs:
			d.expandDeviceRequest(request)
		case request := <-d.deviceControlJobs:
			d.expandDeviceControlRequest(request)
		case request := <-d.realnameJobs:
			d.expandRealnameRequest(request)
		}
	}
}

func (d *BestEffortSeriesDispatcher) expandDeviceControlRequest(request cardapp.DeviceControlSeriesRequest) {
	deviceRequest := request.Request
	deviceRequest.ResourceType = constants.CardObservationResourceTypeDevice
	deviceRequest.ResourceID = strconv.FormatUint(uint64(request.DeviceID), 10)
	deviceRequest.SyncType = constants.CardObservationSyncTypeDeviceInfo
	deviceRequest.ExpectedValue = strings.TrimSpace(request.TargetICCID)
	d.triggerRequest(deviceRequest)
	if strings.TrimSpace(request.TargetICCID) == "" {
		for _, cardID := range request.BoundCardIDs {
			d.triggerCardControlRequest(request.Request, cardID, constants.CardObservationSyncTypeNetwork)
		}
		return
	}
	d.triggerCardControlRequest(request.Request, request.SourceCardID, constants.CardObservationSyncTypeNetwork)
	if request.TargetCardID != request.SourceCardID {
		d.triggerCardControlRequest(request.Request, request.TargetCardID, constants.CardObservationSyncTypeNetwork)
	}
	if request.IncludeTargetTraffic {
		d.triggerCardControlRequest(request.Request, request.TargetCardID, constants.CardObservationSyncTypeTraffic)
	}
}

func (d *BestEffortSeriesDispatcher) triggerCardControlRequest(base cardapp.SeriesRequest, cardID uint, syncType string) {
	if cardID == 0 {
		return
	}
	base.ResourceType = constants.CardObservationResourceTypeCard
	base.ResourceID = strconv.FormatUint(uint64(cardID), 10)
	base.SyncType = syncType
	base.ExpectedValue = ""
	d.triggerRequest(base)
}

func (d *BestEffortSeriesDispatcher) triggerRequest(request cardapp.SeriesRequest) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	seriesID, merged, err := d.trigger.Trigger(ctx, request)
	cancel()
	if err != nil {
		d.logFailure(request, seriesID, merged, err, "后台观测序列触发失败")
	}
}

func (d *BestEffortSeriesDispatcher) expandDeviceRequest(request cardapp.DeviceCardsSeriesRequest) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	bindings, err := d.deviceSimBindingStore.ListByDeviceID(ctx, request.DeviceID)
	cancel()
	if err != nil {
		d.logFailure(request.Request, "", false, err, "查询设备有效绑定卡失败")
		return
	}
	for _, binding := range bindings {
		if binding == nil || binding.IotCardID == 0 {
			continue
		}
		cardRequest := request.Request
		cardRequest.ResourceType = constants.CardObservationResourceTypeCard
		cardRequest.ResourceID = strconv.FormatUint(uint64(binding.IotCardID), 10)
		d.triggerRequest(cardRequest)
	}
}

func (d *BestEffortSeriesDispatcher) expandRealnameRequest(request cardapp.RealnameCapabilitySeriesRequest) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	carrier, err := d.carrierStore.GetByID(ctx, request.CarrierID)
	cancel()
	if err != nil {
		d.logFailure(request.Request, "", false, err, "查询运营商实名能力失败")
		return
	}
	if carrier.RealnameLinkType == constants.RealnameLinkTypeNone {
		return
	}
	d.triggerRequest(request.Request)
}

func normalizeRequest(request cardapp.SeriesRequest) cardapp.SeriesRequest {
	request.RequestID = strings.TrimSpace(request.RequestID)
	request.CorrelationID = strings.TrimSpace(request.CorrelationID)
	if request.RequestID == "" && request.CorrelationID == "" {
		request.RequestID = uuid.NewString()
		request.CorrelationID = request.RequestID
	} else if request.RequestID == "" {
		request.RequestID = request.CorrelationID
	} else if request.CorrelationID == "" {
		request.CorrelationID = request.RequestID
	}
	return request
}

func (d *BestEffortSeriesDispatcher) logFailure(request cardapp.SeriesRequest, seriesID string, merged bool, err error, message string) {
	if d.logger == nil {
		return
	}
	fields := []zap.Field{
		zap.String("scene", request.Scene),
		zap.String("resource_type", request.ResourceType),
		zap.String("resource_id", request.ResourceID),
		zap.String("sync_type", request.SyncType),
		zap.String("series_id", seriesID),
		zap.Bool("merged", merged),
		zap.String("request_id", request.RequestID),
		zap.String("correlation_id", request.CorrelationID),
	}
	if err != nil {
		fields = append(fields, zap.Error(err))
	}
	d.logger.Warn(message+"，已保持原查询结果", fields...)
}

var _ cardapp.BestEffortSeriesDispatcher = (*BestEffortSeriesDispatcher)(nil)
