package appconfig

import (
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/repository"
)

const h5LinkGroup = "h5-links"

var h5LinkDefinitions = []H5LinkDefinition{
	{Key: "host-center", Label: "Host Center"},
	{Key: "bd-center", Label: "BD Center"},
	{Key: "bd-leader-center", Label: "BD Leader Center"},
	{Key: "agency-center", Label: "Agency Center"},
	{Key: "invite-user", Label: "Invite User"},
}

type AppConfigService struct {
	store *repository.Store
}

type H5LinkDefinition struct {
	Key   string `json:"key"`
	Label string `json:"label"`
}

type H5Link struct {
	Key         string `json:"key"`
	Label       string `json:"label"`
	URL         string `json:"url"`
	UpdatedAtMs int64  `json:"updatedAtMs"`
}

type AppBanner struct {
	ID          uint   `json:"id"`
	AppCode     string `json:"appCode"`
	CoverURL    string `json:"coverUrl"`
	BannerType  string `json:"bannerType"`
	Param       string `json:"param"`
	Status      string `json:"status"`
	Platform    string `json:"platform"`
	SortOrder   int    `json:"sortOrder"`
	RegionID    int64  `json:"regionId"`
	CountryCode string `json:"countryCode"`
	Description string `json:"description"`
	CreatedAtMs int64  `json:"createdAtMs"`
	UpdatedAtMs int64  `json:"updatedAtMs"`
}

type AppVersion struct {
	ID          uint   `json:"id"`
	AppCode     string `json:"appCode"`
	Platform    string `json:"platform"`
	Version     string `json:"version"`
	BuildNumber int64  `json:"buildNumber"`
	ForceUpdate bool   `json:"forceUpdate"`
	DownloadURL string `json:"downloadUrl"`
	Description string `json:"description"`
	CreatedAtMs int64  `json:"createdAtMs"`
	UpdatedAtMs int64  `json:"updatedAtMs"`
}

func NewService(store *repository.Store) *AppConfigService {
	return &AppConfigService{store: store}
}

func (s *AppConfigService) ListH5Links() ([]H5Link, error) {
	configs, err := s.store.ListAppConfigs(h5LinkGroup)
	if err != nil {
		return nil, err
	}

	configByKey := make(map[string]model.AppConfig, len(configs))
	for _, config := range configs {
		configByKey[config.Key] = config
	}

	items := make([]H5Link, 0, len(h5LinkDefinitions))
	for _, definition := range h5LinkDefinitions {
		item := H5Link{
			Key:   definition.Key,
			Label: definition.Label,
		}
		if config, ok := configByKey[definition.Key]; ok {
			item.URL = config.Value
			item.UpdatedAtMs = config.UpdatedAtMS
		}
		items = append(items, item)
	}
	return items, nil
}

func (s *AppConfigService) UpdateH5Links(request updateH5LinksRequest) ([]H5Link, error) {
	if len(request.Items) == 0 {
		return nil, errors.New("items are required")
	}

	allowed := h5LinkDefinitionSet()
	items := make([]model.AppConfig, 0, len(request.Items))
	seen := make(map[string]struct{}, len(request.Items))
	for _, item := range request.Items {
		key := strings.TrimSpace(item.Key)
		if _, ok := allowed[key]; !ok {
			return nil, fmt.Errorf("invalid h5 link key: %s", key)
		}
		if _, ok := seen[key]; ok {
			return nil, fmt.Errorf("duplicate h5 link key: %s", key)
		}
		seen[key] = struct{}{}

		link := strings.TrimSpace(item.URL)
		if err := validateH5URL(link); err != nil {
			return nil, err
		}
		items = append(items, model.AppConfig{
			Group:       h5LinkGroup,
			Key:         key,
			Value:       link,
			Description: allowed[key],
		})
	}

	if err := s.store.UpsertAppConfigs(items); err != nil {
		return nil, err
	}
	return s.ListH5Links()
}

func (s *AppConfigService) ListBanners(appCode string, options repository.AppBannerListOptions) ([]AppBanner, error) {
	options.AppCode = appctx.Normalize(appCode)
	options.Status = normalizeBannerStatus(options.Status)
	options.Platform = normalizeBannerPlatform(options.Platform)
	options.Country = normalizeCountryCode(options.Country)
	options.Keyword = strings.TrimSpace(options.Keyword)
	items, err := s.store.ListAppBanners(options)
	if err != nil {
		return nil, err
	}
	out := make([]AppBanner, 0, len(items))
	for _, item := range items {
		out = append(out, appBannerFromModel(item))
	}
	return out, nil
}

func (s *AppConfigService) CreateBanner(appCode string, req bannerRequest) (AppBanner, error) {
	item, err := bannerModelFromRequest(appctx.Normalize(appCode), req)
	if err != nil {
		return AppBanner{}, err
	}
	if err := s.store.CreateAppBanner(&item); err != nil {
		return AppBanner{}, err
	}
	return appBannerFromModel(item), nil
}

func (s *AppConfigService) UpdateBanner(appCode string, id uint, req bannerRequest) (AppBanner, error) {
	item, err := s.store.GetAppBanner(appctx.Normalize(appCode), id)
	if err != nil {
		return AppBanner{}, err
	}
	updated, err := bannerModelFromRequest(item.AppCode, req)
	if err != nil {
		return AppBanner{}, err
	}
	item.CoverURL = updated.CoverURL
	item.BannerType = updated.BannerType
	item.Param = updated.Param
	item.Status = updated.Status
	item.Platform = updated.Platform
	item.SortOrder = updated.SortOrder
	item.RegionID = updated.RegionID
	item.CountryCode = updated.CountryCode
	item.Description = updated.Description
	if err := s.store.UpdateAppBanner(&item); err != nil {
		return AppBanner{}, err
	}
	return appBannerFromModel(item), nil
}

func (s *AppConfigService) DeleteBanner(appCode string, id uint) error {
	return s.store.DeleteAppBanner(appctx.Normalize(appCode), id)
}

func (s *AppConfigService) ListAppVersions(appCode string, options repository.AppVersionListOptions) ([]AppVersion, error) {
	options.AppCode = appctx.Normalize(appCode)
	options.Platform = normalizeAppPlatform(options.Platform)
	options.Keyword = strings.TrimSpace(options.Keyword)
	items, err := s.store.ListAppVersions(options)
	if err != nil {
		return nil, err
	}
	out := make([]AppVersion, 0, len(items))
	for _, item := range items {
		out = append(out, appVersionFromModel(item))
	}
	return out, nil
}

func (s *AppConfigService) CreateAppVersion(appCode string, req appVersionRequest) (AppVersion, error) {
	item, err := appVersionModelFromRequest(appctx.Normalize(appCode), req)
	if err != nil {
		return AppVersion{}, err
	}
	if err := s.store.CreateAppVersion(&item); err != nil {
		return AppVersion{}, err
	}
	return appVersionFromModel(item), nil
}

func (s *AppConfigService) UpdateAppVersion(appCode string, id uint, req appVersionRequest) (AppVersion, error) {
	item, err := s.store.GetAppVersion(appctx.Normalize(appCode), id)
	if err != nil {
		return AppVersion{}, err
	}
	updated, err := appVersionModelFromRequest(item.AppCode, req)
	if err != nil {
		return AppVersion{}, err
	}
	item.Platform = updated.Platform
	item.Version = updated.Version
	item.BuildNumber = updated.BuildNumber
	item.ForceUpdate = updated.ForceUpdate
	item.DownloadURL = updated.DownloadURL
	item.Description = updated.Description
	if err := s.store.UpdateAppVersion(&item); err != nil {
		return AppVersion{}, err
	}
	return appVersionFromModel(item), nil
}

func (s *AppConfigService) DeleteAppVersion(appCode string, id uint) error {
	return s.store.DeleteAppVersion(appctx.Normalize(appCode), id)
}

func h5LinkDefinitionSet() map[string]string {
	out := make(map[string]string, len(h5LinkDefinitions))
	for _, definition := range h5LinkDefinitions {
		out[definition.Key] = definition.Label
	}
	return out
}

func validateH5URL(value string) error {
	if value == "" {
		return nil
	}
	if len(value) > 2048 {
		return errors.New("h5 link is too long")
	}
	for _, char := range value {
		if unicode.IsSpace(char) {
			return errors.New("h5 link cannot contain whitespace")
		}
	}
	return nil
}

func bannerModelFromRequest(appCode string, req bannerRequest) (model.AppBanner, error) {
	item := model.AppBanner{
		AppCode:     appctx.Normalize(appCode),
		CoverURL:    strings.TrimSpace(req.CoverURL),
		BannerType:  normalizeBannerType(req.BannerType),
		Param:       strings.TrimSpace(req.Param),
		Status:      normalizeBannerStatus(req.Status),
		Platform:    normalizeBannerPlatform(req.Platform),
		SortOrder:   req.SortOrder,
		RegionID:    req.RegionID,
		CountryCode: normalizeCountryCode(req.CountryCode),
		Description: strings.TrimSpace(req.Description),
	}
	if item.CoverURL == "" || len(item.CoverURL) > 1024 {
		return model.AppBanner{}, errors.New("banner cover is invalid")
	}
	if item.BannerType != "h5" && item.BannerType != "app" {
		return model.AppBanner{}, errors.New("banner type is invalid")
	}
	if item.BannerType == "h5" {
		if item.Param == "" {
			return model.AppBanner{}, errors.New("h5 link is required")
		}
		if err := validateH5URL(item.Param); err != nil {
			return model.AppBanner{}, err
		}
	}
	if len(item.Param) > 2048 {
		return model.AppBanner{}, errors.New("banner param is too long")
	}
	if item.Status != "active" && item.Status != "disabled" {
		return model.AppBanner{}, errors.New("banner status is invalid")
	}
	if item.Platform != "android" && item.Platform != "ios" {
		return model.AppBanner{}, errors.New("banner platform is invalid")
	}
	if item.RegionID < 0 {
		return model.AppBanner{}, errors.New("banner region is invalid")
	}
	if item.CountryCode != "" && !validCountryCode(item.CountryCode) {
		return model.AppBanner{}, errors.New("banner country is invalid")
	}
	if utf8.RuneCountInString(item.Description) > 255 {
		return model.AppBanner{}, errors.New("banner description is too long")
	}
	return item, nil
}

func appBannerFromModel(item model.AppBanner) AppBanner {
	return AppBanner{
		ID:          item.ID,
		AppCode:     item.AppCode,
		CoverURL:    item.CoverURL,
		BannerType:  item.BannerType,
		Param:       item.Param,
		Status:      item.Status,
		Platform:    item.Platform,
		SortOrder:   item.SortOrder,
		RegionID:    item.RegionID,
		CountryCode: item.CountryCode,
		Description: item.Description,
		CreatedAtMs: item.CreatedAtMS,
		UpdatedAtMs: item.UpdatedAtMS,
	}
}

func appVersionModelFromRequest(appCode string, req appVersionRequest) (model.AppVersion, error) {
	item := model.AppVersion{
		AppCode:     appctx.Normalize(appCode),
		Platform:    normalizeAppPlatform(req.Platform),
		Version:     strings.TrimSpace(req.Version),
		BuildNumber: req.BuildNumber,
		ForceUpdate: req.ForceUpdate,
		DownloadURL: strings.TrimSpace(req.DownloadURL),
		Description: strings.TrimSpace(req.Description),
	}
	if item.Platform != "android" && item.Platform != "ios" {
		return model.AppVersion{}, errors.New("app version platform is invalid")
	}
	if item.Version == "" || utf8.RuneCountInString(item.Version) > 40 {
		return model.AppVersion{}, errors.New("app version is invalid")
	}
	if item.BuildNumber <= 0 {
		return model.AppVersion{}, errors.New("app build number is invalid")
	}
	if item.DownloadURL == "" || len(item.DownloadURL) > 2048 {
		return model.AppVersion{}, errors.New("app download url is invalid")
	}
	for _, char := range item.DownloadURL {
		if unicode.IsSpace(char) {
			return model.AppVersion{}, errors.New("app download url cannot contain whitespace")
		}
	}
	if utf8.RuneCountInString(item.Description) > 1000 {
		return model.AppVersion{}, errors.New("app version description is too long")
	}
	return item, nil
}

func appVersionFromModel(item model.AppVersion) AppVersion {
	return AppVersion{
		ID:          item.ID,
		AppCode:     item.AppCode,
		Platform:    item.Platform,
		Version:     item.Version,
		BuildNumber: item.BuildNumber,
		ForceUpdate: item.ForceUpdate,
		DownloadURL: item.DownloadURL,
		Description: item.Description,
		CreatedAtMs: item.CreatedAtMS,
		UpdatedAtMs: item.UpdatedAtMS,
	}
}

func normalizeBannerType(value string) string {
	return strings.ToLower(strings.TrimSpace(value))
}

func normalizeBannerStatus(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if value == "" {
		return "active"
	}
	return value
}

func normalizeBannerPlatform(value string) string {
	return strings.ToLower(strings.TrimSpace(value))
}

func normalizeAppPlatform(value string) string {
	return strings.ToLower(strings.TrimSpace(value))
}

func normalizeCountryCode(value string) string {
	return strings.ToUpper(strings.TrimSpace(value))
}

func validCountryCode(value string) bool {
	if len(value) < 2 || len(value) > 3 {
		return false
	}
	for _, char := range value {
		if char < 'A' || char > 'Z' {
			return false
		}
	}
	return true
}
