package nextgenmetering

import (
	"fmt"
	"strings"

	"github.com/tidbcloud/metering/pkg/common/models"
	"go.uber.org/zap"
)

type EssentialResourceManagerHandler struct {
	gen *SKUGenerator
	log *zap.Logger
}

func NewEssentialResourceManagerHandler(gen *SKUGenerator, log *zap.Logger) *EssentialResourceManagerHandler {
	return &EssentialResourceManagerHandler{gen: gen, log: log}
}

func (h *EssentialResourceManagerHandler) GenerateSKUs(dataPoint map[string]interface{}, params *GenerateSKUParams) ([]models.BillingReportV2Deltalake, error) {
	if !validateEssentialParams("EssentialResourceManagerHandler", params, h.log) {
		return []models.BillingReportV2Deltalake{}, nil
	}
	records, err := h.gen.GenerateResourceManagerSKUs(
		dataPoint, params.ClusterID, params.SharedPoolID, params.TenantID, params.Timestamp,
		params.CloudProvider, params.Region, params.Plan,
		params.EnrichmentData, params.BackupMetaData, params.Now,
	)
	if err != nil {
		return nil, err
	}
	rcuRecords, err := convertEssentialRCU(records, params, h.log)
	if err != nil {
		return nil, err
	}
	backupRecords := convertEssentialBackup(records, h.log)
	return append(rcuRecords, backupRecords...), nil
}

type EssentialStorageSizeHandler struct {
	gen *SKUGenerator
	log *zap.Logger
}

func NewEssentialStorageSizeHandler(gen *SKUGenerator, log *zap.Logger) *EssentialStorageSizeHandler {
	return &EssentialStorageSizeHandler{gen: gen, log: log}
}

func (h *EssentialStorageSizeHandler) GenerateSKUs(dataPoint map[string]interface{}, params *GenerateSKUParams) ([]models.BillingReportV2Deltalake, error) {
	if !validateEssentialParams("EssentialStorageSizeHandler", params, h.log) {
		return []models.BillingReportV2Deltalake{}, nil
	}
	records, err := h.gen.GenerateStorageSizeSKUs(
		dataPoint, params.ClusterID, params.SharedPoolID, params.TenantID, params.Timestamp,
		params.CloudProvider, params.Region, params.Plan,
		params.EnrichmentData, params.Now,
	)
	if err != nil {
		return nil, err
	}
	return convertEssentialStorage(records, params, h.log), nil
}

type EssentialProxyHandler struct {
	gen *SKUGenerator
	log *zap.Logger
}

func NewEssentialProxyHandler(gen *SKUGenerator, log *zap.Logger) *EssentialProxyHandler {
	return &EssentialProxyHandler{gen: gen, log: log}
}

func (h *EssentialProxyHandler) GenerateSKUs(dataPoint map[string]interface{}, params *GenerateSKUParams) ([]models.BillingReportV2Deltalake, error) {
	if !validateEssentialParams("EssentialProxyHandler", params, h.log) {
		return []models.BillingReportV2Deltalake{}, nil
	}
	records, err := h.gen.GenerateProxySKUs(
		dataPoint, params.ClusterID, params.SharedPoolID, params.TenantID, params.Timestamp,
		params.CloudProvider, params.Region, params.Plan,
		params.EnrichmentData, params.Now,
	)
	if err != nil {
		return nil, err
	}
	return convertEssentialDataTransferOut(records, h.log), nil
}

type EssentialDFSStatsHandler struct {
	gen *SKUGenerator
	log *zap.Logger
}

func NewEssentialDFSStatsHandler(gen *SKUGenerator, log *zap.Logger) *EssentialDFSStatsHandler {
	return &EssentialDFSStatsHandler{gen: gen, log: log}
}

func (h *EssentialDFSStatsHandler) GenerateSKUs(dataPoint map[string]interface{}, params *GenerateSKUParams) ([]models.BillingReportV2Deltalake, error) {
	if !validateEssentialParams("EssentialDFSStatsHandler", params, h.log) {
		return []models.BillingReportV2Deltalake{}, nil
	}
	records, err := h.gen.GenerateBackupStorageSKUs(
		dataPoint, params.ClusterID, params.SharedPoolID, params.TenantID, params.Timestamp,
		params.CloudProvider, params.Region, params.Plan,
		params.EnrichmentData, params.Now,
	)
	if err != nil {
		return nil, err
	}
	return convertEssentialBackup(records, h.log), nil
}

type EssentialFlexBackupHandler struct {
	gen *SKUGenerator
	log *zap.Logger
}

func NewEssentialFlexBackupHandler(gen *SKUGenerator, log *zap.Logger) *EssentialFlexBackupHandler {
	return &EssentialFlexBackupHandler{gen: gen, log: log}
}

func (h *EssentialFlexBackupHandler) GenerateSKUs(dataPoint map[string]interface{}, params *GenerateSKUParams) ([]models.BillingReportV2Deltalake, error) {
	if !validateEssentialParams("EssentialFlexBackupHandler", params, h.log) {
		return []models.BillingReportV2Deltalake{}, nil
	}
	return h.gen.GenerateFlexBackupStorageSKUs(
		dataPoint, params.ClusterID, params.SharedPoolID, params.TenantID, params.Timestamp,
		params.CloudProvider, params.Region, params.Plan,
		params.EnrichmentData, params.Now,
	)
}

type EssentialManualBackupHandler struct {
	gen *SKUGenerator
	log *zap.Logger
}

func NewEssentialManualBackupHandler(gen *SKUGenerator, log *zap.Logger) *EssentialManualBackupHandler {
	return &EssentialManualBackupHandler{gen: gen, log: log}
}

func (h *EssentialManualBackupHandler) GenerateSKUs(dataPoint map[string]interface{}, params *GenerateSKUParams) ([]models.BillingReportV2Deltalake, error) {
	if !validateEssentialParams("EssentialManualBackupHandler", params, h.log) {
		return []models.BillingReportV2Deltalake{}, nil
	}
	return h.gen.GenerateManualBackupStorageSKUs(
		dataPoint, params.ClusterID, params.SharedPoolID, params.TenantID, params.Timestamp,
		params.CloudProvider, params.Region, params.Plan,
		params.EnrichmentData, params.Now,
	)
}

func validateEssentialParams(handler string, params *GenerateSKUParams, log *zap.Logger) bool {
	if params.ClusterID == "" {
		log.Warn("essential params validation failed, will skip", zap.String("handler", handler), zap.String("reason", "clusterID is required"))
		return false
	}
	if params.TenantID == 0 {
		log.Warn("essential params validation failed, will skip", zap.String("handler", handler), zap.String("reason", "tenantID is required"))
		return false
	}
	return true
}

// convertEssentialRCU converts RCU SKU from premium to essential format.
// Before: rcu:aws:us-east-1:{plan}:all:total
// After:  serverless-request-capacity-unit:aws:us-east-1:{plan}
func convertEssentialRCU(records []models.BillingReportV2Deltalake, params *GenerateSKUParams, log *zap.Logger) ([]models.BillingReportV2Deltalake, error) {
	result := make([]models.BillingReportV2Deltalake, 0, len(records))
	for _, r := range records {
		if strings.HasPrefix(r.Unit, models.NextGenSKURCU) && strings.Contains(r.Unit, "all:total") {
			r.Unit = fmt.Sprintf("%s:%s:%s:%s", models.NextGenSKUServerlessRequestCapacityUnit, params.CloudProvider, params.Region, params.Plan)
			result = append(result, r)
			break
		}
	}
	log.Debug("convertEssentialRCU", zap.Any("input", records), zap.Any("output", result))
	return result, nil
}

// convertEssentialStorage converts storage SKUs to essential format.
// Mapping rules:
//
//	disk-storage-size:{provider}:{region}:{plan}:row:*                    → serverless-row-storage:{provider}:{region}:{plan}
//	disk-storage-size:{provider}:{region}:{plan}:column:*                 → serverless-column-storage:{provider}:{region}:{plan}
//	disk-storage-size-dual-encryption:{provider}:{region}:{plan}:row:*    → serverless-row-storage-dual-encryption:{provider}:{region}:{plan}
//	disk-storage-size-dual-encryption:{provider}:{region}:{plan}:column:* → serverless-column-storage-dual-encryption:{provider}:{region}:{plan}
//
// Layout field (parts[4]) determines row vs column.
func convertEssentialStorage(records []models.BillingReportV2Deltalake, params *GenerateSKUParams, log *zap.Logger) []models.BillingReportV2Deltalake {
	result := make([]models.BillingReportV2Deltalake, 0, len(records))
	for _, r := range records {
		if !strings.HasPrefix(r.Unit, models.NextGenSKUDiskStorageSize) {
			continue
		}

		parts := strings.Split(r.Unit, ":")
		if len(parts) < 5 {
			continue
		}

		isDualEncryption := parts[0] == models.NextGenSKUDiskStorageSizeDualEncryption

		layout := parts[4]
		var prefix string
		switch layout {
		case models.NextGenStorageLayoutRow:
			if isDualEncryption {
				prefix = models.NextGenSKUServerlessRowStorageDualEncryption
			} else {
				prefix = models.NextGenSKUServerlessRowStorage
			}
		case models.NextGenStorageLayoutColumn:
			if isDualEncryption {
				prefix = models.NextGenSKUServerlessColumnStorageDualEncryption
			} else {
				prefix = models.NextGenSKUServerlessColumnStorage
			}
		default:
			continue
		}

		r.Unit = fmt.Sprintf("%s:%s:%s:%s", prefix, params.CloudProvider, params.Region, params.Plan)
		result = append(result, r)
	}
	log.Debug("convertEssentialStorage", zap.Any("input", records), zap.Any("output", result))
	return result
}

// convertEssentialDataTransferOut filters data-transfer-out records only.
// Unit is unchanged since plan is already passed correctly from params.
func convertEssentialDataTransferOut(records []models.BillingReportV2Deltalake, log *zap.Logger) []models.BillingReportV2Deltalake {
	result := make([]models.BillingReportV2Deltalake, 0, len(records))
	for _, r := range records {
		if !strings.HasPrefix(r.Unit, models.NextGenSKUDataTransferOut) {
			continue
		}
		result = append(result, r)
	}
	log.Debug("convertEssentialDataTransferOut", zap.Any("input", records), zap.Any("output", result))
	return result
}

// convertEssentialBackup filters backup-storage records only.
// Unit is unchanged since plan is already passed correctly from params.
func convertEssentialBackup(records []models.BillingReportV2Deltalake, log *zap.Logger) []models.BillingReportV2Deltalake {
	result := make([]models.BillingReportV2Deltalake, 0, len(records))
	for _, r := range records {
		if !strings.HasPrefix(r.Unit, models.NextGenSKUBackupStorage) {
			continue
		}
		result = append(result, r)
	}
	log.Debug("convertEssentialBackup", zap.Any("input", records), zap.Any("output", result))
	return result
}
