package giftdiamond

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"
)

var giftTypes = []string{"normal", "lucky", "super_lucky"}

type Service struct {
	db *sql.DB
}

func NewService(db *sql.DB) *Service {
	return &Service{db: db}
}

func (s *Service) List(ctx context.Context, appCode string, regionID int64) (ratioListDTO, error) {
	if err := s.ensureSchema(ctx); err != nil {
		return ratioListDTO{}, err
	}
	items := make([]ratioDTO, 0, len(giftTypes))
	for _, giftType := range giftTypes {
		item, configured, err := s.resolveRatio(ctx, appCode, regionID, giftType)
		if err != nil {
			return ratioListDTO{}, err
		}
		if configured {
			items = append(items, item)
		}
	}
	return ratioListDTO{RegionID: regionID, Items: items}, nil
}

func (s *Service) Update(ctx context.Context, appCode string, req updateRequest, adminID int64) (ratioListDTO, error) {
	if req.RegionID < 0 {
		return ratioListDTO{}, errors.New("区域不正确")
	}
	if err := s.ensureSchema(ctx); err != nil {
		return ratioListDTO{}, err
	}
	nowMs := time.Now().UnixMilli()
	tx, err := s.db.BeginTx(ctx, nil)
	if err != nil {
		return ratioListDTO{}, err
	}
	defer func() { _ = tx.Rollback() }()
	for _, giftType := range giftTypes {
		raw, exists := req.Ratios[giftType]
		if !exists {
			return ratioListDTO{}, fmt.Errorf("%s 主播钻石比例不能为空", giftType)
		}
		ratio, err := normalizePercent(raw)
		if err != nil {
			return ratioListDTO{}, fmt.Errorf("%s 主播钻石比例不正确", giftType)
		}
		rawReturnCoin, exists := req.ReturnCoinRatios[giftType]
		if !exists {
			return ratioListDTO{}, fmt.Errorf("%s 返还金币比例不能为空", giftType)
		}
		returnCoinRatio, err := normalizePercent(rawReturnCoin)
		if err != nil {
			return ratioListDTO{}, fmt.Errorf("%s 返还金币比例不正确", giftType)
		}
		if _, err := tx.ExecContext(ctx, `
			INSERT INTO gift_diamond_ratio_configs (
			  app_code, region_id, gift_type_code, status, ratio_percent, coin_return_ratio_percent,
			  created_by_admin_id, updated_by_admin_id, created_at_ms, updated_at_ms
			) VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)
			ON DUPLICATE KEY UPDATE
			  status = VALUES(status),
			  ratio_percent = VALUES(ratio_percent),
			  coin_return_ratio_percent = VALUES(coin_return_ratio_percent),
			  updated_by_admin_id = VALUES(updated_by_admin_id),
			  updated_at_ms = VALUES(updated_at_ms)`,
			appCode, req.RegionID, giftType, ratio, returnCoinRatio, adminID, adminID, nowMs, nowMs,
		); err != nil {
			return ratioListDTO{}, err
		}
	}
	if err := tx.Commit(); err != nil {
		return ratioListDTO{}, err
	}
	return s.List(ctx, appCode, req.RegionID)
}

func (s *Service) resolveRatio(ctx context.Context, appCode string, regionID int64, giftType string) (ratioDTO, bool, error) {
	if item, exists, err := s.getRatio(ctx, appCode, regionID, giftType); err != nil || exists {
		return item, exists, err
	}
	if regionID != 0 {
		if item, exists, err := s.getRatio(ctx, appCode, 0, giftType); err != nil || exists {
			item.RegionID = regionID
			return item, exists, err
		}
	}
	// 后台必须如实展示运行库状态；没有区域配置且没有全局继承时返回未配置，
	// 不能合成一组未持久化的 100/10/1 让运营误以为送礼链路已有政策。
	return ratioDTO{}, false, nil
}

func (s *Service) getRatio(ctx context.Context, appCode string, regionID int64, giftType string) (ratioDTO, bool, error) {
	var item ratioDTO
	var percent string
	var returnCoinPercent string
	err := s.db.QueryRowContext(ctx, `
		SELECT region_id, gift_type_code, CAST(ratio_percent AS CHAR), CAST(coin_return_ratio_percent AS CHAR), status, updated_at_ms
		FROM gift_diamond_ratio_configs
		WHERE app_code = ? AND region_id = ? AND gift_type_code = ?
		LIMIT 1`, appCode, regionID, giftType).Scan(&item.EffectiveRegionID, &item.GiftTypeCode, &percent, &returnCoinPercent, &item.Status, &item.UpdatedAtMS)
	if errors.Is(err, sql.ErrNoRows) {
		return ratioDTO{}, false, nil
	}
	if err != nil {
		return ratioDTO{}, false, err
	}
	item.RegionID = regionID
	item.RatioPercent, err = persistedPercentString(percent)
	if err != nil {
		return ratioDTO{}, false, fmt.Errorf("%s 主播钻石比例存储值不正确: %w", giftType, err)
	}
	item.ReturnCoinRatioPercent, err = persistedPercentString(returnCoinPercent)
	if err != nil {
		return ratioDTO{}, false, fmt.Errorf("%s 返还金币比例存储值不正确: %w", giftType, err)
	}
	return item, true, nil
}

func normalizePercent(raw interface{}) (string, error) {
	text := strings.TrimSpace(fmt.Sprint(raw))
	if text == "" {
		return "", errors.New("empty")
	}
	value, err := strconv.ParseFloat(text, 64)
	if err != nil || math.IsNaN(value) || math.IsInf(value, 0) || value < 0 || value > 100 {
		return "", errors.New("invalid")
	}
	return fmt.Sprintf("%.2f", value), nil
}

func persistedPercentString(raw string) (string, error) {
	value, err := strconv.ParseFloat(strings.TrimSpace(raw), 64)
	if err != nil || math.IsNaN(value) || math.IsInf(value, 0) || value < 0 || value > 100 {
		return "", errors.New("invalid persisted percent")
	}
	return fmt.Sprintf("%.2f", value), nil
}

func (s *Service) ensureSchema(ctx context.Context) error {
	if s == nil || s.db == nil {
		return errors.New("wallet db is not configured")
	}
	if _, err := s.db.ExecContext(ctx, `
		CREATE TABLE IF NOT EXISTS gift_diamond_ratio_configs (
		  app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码，用于多租户隔离',
		  region_id BIGINT NOT NULL DEFAULT 0 COMMENT '房间可见区域，0 表示全局默认',
		  gift_type_code VARCHAR(32) NOT NULL COMMENT '礼物类型编码',
		  status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '业务状态',
		  ratio_percent DECIMAL(5,2) NOT NULL DEFAULT 100.00 COMMENT '房间贡献热度和主播周期钻石入账比例，百分比',
		  coin_return_ratio_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '收礼人金币返还比例，百分比',
		  created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台用户 ID',
		  updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新后台用户 ID',
		  created_at_ms BIGINT NOT NULL COMMENT '创建时间，UTC epoch ms',
		  updated_at_ms BIGINT NOT NULL COMMENT '更新时间，UTC epoch ms',
		  PRIMARY KEY (app_code, region_id, gift_type_code),
		  KEY idx_gift_diamond_ratio_region (app_code, region_id, status)
		) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='礼物钻石和房间贡献比例配置表'`); err != nil {
		return err
	}
	if _, err := ensureReturnCoinRatioColumn(ctx, s.db); err != nil {
		return err
	}
	// 这里只维护结构，不按任何 App 名创建倍率记录；Update 是唯一新增/覆盖运行政策的入口。
	// 存量正式配置不会被删除或重写。
	return nil
}

func ensureReturnCoinRatioColumn(ctx context.Context, db *sql.DB) (bool, error) {
	// 该列和 ratio_percent 共用区域/礼物类型维度，但服务于收礼人金币收入，避免后台调整主播钻石时误改用户返还。
	if _, err := db.ExecContext(ctx, `
		ALTER TABLE gift_diamond_ratio_configs
		ADD COLUMN coin_return_ratio_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '收礼人金币返还比例，百分比' AFTER ratio_percent`); err != nil {
		if strings.Contains(strings.ToLower(err.Error()), "duplicate column") {
			return false, nil
		}
		return false, err
	}
	return true, nil
}
