package platform

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"strings"
)

type vehicleSourcePolicyStore interface {
	VehicleSourcePolicy(context.Context, string) (VehicleSourcePolicyConfig, error)
	SaveVehicleSourcePolicy(context.Context, vehicleSourcePolicyStoreUpdate) (VehicleSourcePolicyConfig, error)
	VehicleLocationSourceHistory(context.Context, string) ([]vehicleLocationSourceHistory, error)
}

func sourceReference(vin, protocol, sourceKey string) string {
	sum := sha256.Sum256([]byte(strings.TrimSpace(vin) + "\x00" + strings.TrimSpace(protocol) + "\x00" + sourceKey))
	return hex.EncodeToString(sum[:])
}

func (s *Service) VehicleSourceDiagnostic(ctx context.Context, vin string) (VehicleSourceDiagnostic, error) {
	if err := authorizeInternalOperations(ctx, false); err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	evidence, err := s.VehicleSourceEvidence(ctx, vin, "")
	if err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	store, ok := s.store.(vehicleSourcePolicyStore)
	if !ok {
		return VehicleSourceDiagnostic{}, errors.New("vehicle source policy store is not configured")
	}
	history, err := store.VehicleLocationSourceHistory(ctx, evidence.VIN)
	if err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	historyBySource := make(map[string]vehicleLocationSourceHistory, len(history))
	for _, item := range history {
		historyBySource[item.Protocol+"\x00"+item.sourceKey] = item
	}
	var access *AccessVehicleRow
	page, accessErr := s.AccessVehicles(ctx, AccessQuery{Keyword: evidence.VIN, Limit: 10})
	if accessErr == nil {
		for index := range page.Items {
			if page.Items[index].VIN == evidence.VIN {
				row := page.Items[index]
				access = &row
				break
			}
		}
	}
	protocolStatus := map[string]AccessProtocolStatus{}
	if access != nil {
		for _, status := range access.ProtocolStatuses {
			protocolStatus[status.Protocol] = status
		}
	}
	for index := range evidence.LocationSources {
		source := &evidence.LocationSources[index]
		if !strings.EqualFold(source.SourceKind, "CANONICAL") {
			source.SourceRef = sourceReference(evidence.VIN, source.Protocol, source.sourceKey)
		}
		if item, exists := historyBySource[source.Protocol+"\x00"+source.sourceKey]; exists {
			source.FirstSeenAt = item.FirstSeenAt
			source.ReportSampleCount = item.ReportSampleCount
		}
		if status, exists := protocolStatus[source.Protocol]; exists {
			source.ReportIntervalSec = status.ReportIntervalSec
			if source.FirstSeenAt == "" {
				source.FirstSeenAt = status.FirstSeenAt
			}
		}
		source.SelectionReason = sourceSelectionReason(*source)
	}
	policy, err := store.VehicleSourcePolicy(ctx, evidence.VIN)
	if err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	return VehicleSourceDiagnostic{
		Evidence:             evidence,
		Access:               access,
		Policy:               policy,
		RecommendationReason: recommendationReason(evidence),
		RefreshHint:          "策略保存后由车辆下一次有效上报触发网关重新选举；页面不会伪造即时切换结果。",
	}, nil
}

func (s *Service) UpdateVehicleSourcePolicy(ctx context.Context, vin string, update VehicleSourcePolicyUpdate) (VehicleSourceDiagnostic, error) {
	if err := authorizeInternalOperations(ctx, true); err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	vin = strings.ToUpper(strings.TrimSpace(vin))
	update.SourceRef = strings.ToLower(strings.TrimSpace(update.SourceRef))
	update.Remark = strings.TrimSpace(update.Remark)
	if vin == "" {
		return VehicleSourceDiagnostic{}, clientError{Code: "VEHICLE_VIN_REQUIRED", Message: "车辆 VIN 不能为空"}
	}
	if update.Version < 1 {
		return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_POLICY_VERSION_INVALID", Message: "来源策略版本无效，请刷新后重试"}
	}
	if len(update.SourceRef) != sha256.Size*2 {
		return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_REF_INVALID", Message: "来源引用无效"}
	}
	if _, err := hex.DecodeString(update.SourceRef); err != nil {
		return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_REF_INVALID", Message: "来源引用无效"}
	}
	if update.Priority < 1 || update.Priority > 1000 {
		return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_PRIORITY_INVALID", Message: "来源优先级必须在 1 到 1000 之间"}
	}
	if len([]rune(update.Remark)) > 200 {
		return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_REMARK_TOO_LONG", Message: "策略备注不能超过 200 个字符"}
	}
	update.Actor = strings.TrimSpace(update.Actor)
	if update.Actor == "" {
		update.Actor = ActorFromContext(ctx)
	}
	evidenceStore, ok := s.store.(VehicleSourceEvidenceStore)
	if !ok {
		return VehicleSourceDiagnostic{}, errors.New("vehicle source evidence store is not configured")
	}
	evidence, err := evidenceStore.VehicleSourceEvidence(ctx, vin, "")
	if err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	var selected *VehicleLocationSourceEvidence
	for index := range evidence.LocationSources {
		source := &evidence.LocationSources[index]
		if !strings.EqualFold(source.SourceKind, "CANONICAL") && sourceReference(vin, source.Protocol, source.sourceKey) == update.SourceRef {
			selected = source
			break
		}
	}
	if selected == nil || selected.sourceKey == "" {
		return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_NOT_FOUND", Message: "当前车辆不存在该来源，请刷新后重试"}
	}
	store, ok := s.store.(vehicleSourcePolicyStore)
	if !ok {
		return VehicleSourceDiagnostic{}, errors.New("vehicle source policy store is not configured")
	}
	if _, err := store.SaveVehicleSourcePolicy(ctx, vehicleSourcePolicyStoreUpdate{
		VehicleSourcePolicyUpdate: update,
		VIN:                       vin,
		Protocol:                  selected.Protocol,
		SourceKey:                 selected.sourceKey,
		SourceLabel:               selected.SourceLabel,
		CurrentEnabled:            selected.Enabled,
		CurrentPriority:           selected.Priority,
	}); err != nil {
		return VehicleSourceDiagnostic{}, err
	}
	return s.VehicleSourceDiagnostic(ctx, vin)
}

func authorizeInternalOperations(ctx context.Context, adminOnly bool) error {
	principal, ok := PrincipalFromContext(ctx)
	if !ok {
		return nil
	}
	if principal.UserType == "customer" || principal.Role == "customer" {
		return clientError{Code: "PERMISSION_DENIED", Message: "客户账号无权访问内部来源策略"}
	}
	if adminOnly && principal.Role != "admin" {
		return clientError{Code: "PERMISSION_DENIED", Message: "只有管理员可以修改来源策略"}
	}
	return nil
}

func sourceSelectionReason(source VehicleLocationSourceEvidence) string {
	if strings.EqualFold(source.SourceKind, "CANONICAL") {
		return "这是协议融合结果快照，不是独立终端候选；需要展开实际来源后调整策略。"
	}
	if !source.Enabled {
		return "已由运维策略停用，不参与协议内选举。"
	}
	if source.QualityStatus != "" && !strings.EqualFold(source.QualityStatus, "OK") {
		return fmt.Sprintf("质量状态为 %s，当前不应作为首选；%s", source.QualityStatus, firstNonEmpty(source.QualityReason, "请核对原始证据"))
	}
	if source.Recommended {
		return fmt.Sprintf("当前融合推荐；协议内已选中，策略优先级 %d，并通过在线与质量检查。", source.Priority)
	}
	if source.SelectedWithinProtocol {
		return fmt.Sprintf("当前为 %s 协议内候选，策略优先级 %d；跨协议融合当前推荐其他来源。", source.Protocol, source.Priority)
	}
	if !source.Online {
		return fmt.Sprintf("来源当前离线，策略优先级 %d；在线来源会优先参与选举。", source.Priority)
	}
	return fmt.Sprintf("来源可用，策略优先级 %d；当前候选在新鲜度、连续有效样本或稳定性排序中未胜出。", source.Priority)
}

func recommendationReason(evidence VehicleSourceEvidence) string {
	for _, source := range evidence.LocationSources {
		if source.Recommended {
			if strings.EqualFold(source.SourceKind, "CANONICAL") {
				return fmt.Sprintf("当前推荐 %s 的协议融合结果；本次证据未取得可配置的独立终端候选，不能从该快照反推人工优先级。", source.Protocol)
			}
			return fmt.Sprintf(
				"当前推荐 %s / %s：来源已启用、质量为 %s、协议内选中，策略优先级为 %d。选举还会综合两分钟新鲜度、漂移冲突保护和连续有效样本。",
				source.Protocol, source.SourceLabel, firstNonEmpty(source.QualityStatus, "未知"), source.Priority,
			)
		}
	}
	if len(evidence.LocationSources) == 0 {
		return "当前车辆没有可用于位置选举的来源证据。"
	}
	return "当前没有明确推荐来源；请检查来源启用状态、质量、新鲜度和网关下一次有效上报。"
}
