package service

import (
	"context"
	"net/http"
	"strings"

	"cmr-backend/internal/apperr"
	"cmr-backend/internal/store/postgres"
)

type MapExperienceService struct {
	store *postgres.Store
}

type ListExperienceMapsInput struct {
	Limit int
}

type ExperienceMapSummary struct {
	PlaceID                   string   `json:"placeId"`
	PlaceName                 string   `json:"placeName"`
	MapID                     string   `json:"mapId"`
	MapName                   string   `json:"mapName"`
	CoverURL                  *string  `json:"coverUrl,omitempty"`
	Summary                   *string  `json:"summary,omitempty"`
	DefaultExperienceCount    int      `json:"defaultExperienceCount"`
	DefaultExperienceEventIDs []string `json:"defaultExperienceEventIds"`
}

type ExperienceMapDetail struct {
	PlaceID                string                   `json:"placeId"`
	PlaceName              string                   `json:"placeName"`
	MapID                  string                   `json:"mapId"`
	MapName                string                   `json:"mapName"`
	CoverURL               *string                  `json:"coverUrl,omitempty"`
	Summary                *string                  `json:"summary,omitempty"`
	TileBaseURL            *string                  `json:"tileBaseUrl,omitempty"`
	TileMetaURL            *string                  `json:"tileMetaUrl,omitempty"`
	DefaultExperienceCount int                      `json:"defaultExperienceCount"`
	DefaultExperiences     []ExperienceEventSummary `json:"defaultExperiences"`
}

type ExperienceEventSummary struct {
	EventID              string                    `json:"eventId"`
	Title                string                    `json:"title"`
	Subtitle             *string                   `json:"subtitle,omitempty"`
	EventType            *string                   `json:"eventType,omitempty"`
	Status               string                    `json:"status"`
	StatusCode           string                    `json:"statusCode"`
	CTAText              string                    `json:"ctaText"`
	IsDefaultExperience  bool                      `json:"isDefaultExperience"`
	ShowInEventList      bool                      `json:"showInEventList"`
	CurrentPresentation  *PresentationSummaryView  `json:"currentPresentation,omitempty"`
	CurrentContentBundle *ContentBundleSummaryView `json:"currentContentBundle,omitempty"`
}

func NewMapExperienceService(store *postgres.Store) *MapExperienceService {
	return &MapExperienceService{store: store}
}

func (s *MapExperienceService) ListMaps(ctx context.Context, input ListExperienceMapsInput) ([]ExperienceMapSummary, error) {
	rows, err := s.store.ListMapExperienceRows(ctx, input.Limit)
	if err != nil {
		return nil, err
	}
	return mapExperienceSummaries(rows), nil
}

func (s *MapExperienceService) GetMapDetail(ctx context.Context, mapPublicID string) (*ExperienceMapDetail, error) {
	mapPublicID = strings.TrimSpace(mapPublicID)
	if mapPublicID == "" {
		return nil, apperr.New(http.StatusBadRequest, "invalid_map_id", "map id is required")
	}
	rows, err := s.store.ListMapExperienceRowsByMapPublicID(ctx, mapPublicID)
	if err != nil {
		return nil, err
	}
	if len(rows) == 0 {
		return nil, apperr.New(http.StatusNotFound, "map_not_found", "map not found")
	}
	return buildMapExperienceDetail(rows), nil
}

func mapExperienceSummaries(rows []postgres.MapExperienceRow) []ExperienceMapSummary {
	ordered := make([]string, 0, len(rows))
	index := make(map[string]*ExperienceMapSummary)
	for _, row := range rows {
		item, ok := index[row.MapAssetPublicID]
		if !ok {
			summary := &ExperienceMapSummary{
				PlaceID:                   row.PlacePublicID,
				PlaceName:                 row.PlaceName,
				MapID:                     row.MapAssetPublicID,
				MapName:                   row.MapAssetName,
				CoverURL:                  row.MapCoverURL,
				Summary:                   normalizeOptionalText(row.MapSummary),
				DefaultExperienceEventIDs: []string{},
			}
			index[row.MapAssetPublicID] = summary
			ordered = append(ordered, row.MapAssetPublicID)
			item = summary
		}
		if row.EventPublicID != nil && row.EventIsDefaultExperience {
			if !containsString(item.DefaultExperienceEventIDs, *row.EventPublicID) {
				item.DefaultExperienceEventIDs = append(item.DefaultExperienceEventIDs, *row.EventPublicID)
				item.DefaultExperienceCount++
			}
		}
	}

	result := make([]ExperienceMapSummary, 0, len(ordered))
	for _, id := range ordered {
		result = append(result, *index[id])
	}
	return result
}

func buildMapExperienceDetail(rows []postgres.MapExperienceRow) *ExperienceMapDetail {
	first := rows[0]
	result := &ExperienceMapDetail{
		PlaceID:            first.PlacePublicID,
		PlaceName:          first.PlaceName,
		MapID:              first.MapAssetPublicID,
		MapName:            first.MapAssetName,
		CoverURL:           first.MapCoverURL,
		Summary:            normalizeOptionalText(first.MapSummary),
		TileBaseURL:        first.TileBaseURL,
		TileMetaURL:        first.TileMetaURL,
		DefaultExperiences: make([]ExperienceEventSummary, 0, 4),
	}
	seen := make(map[string]struct{})
	for _, row := range rows {
		if row.EventPublicID == nil || !row.EventIsDefaultExperience {
			continue
		}
		if _, ok := seen[*row.EventPublicID]; ok {
			continue
		}
		seen[*row.EventPublicID] = struct{}{}
		result.DefaultExperiences = append(result.DefaultExperiences, buildExperienceEventSummary(row))
	}
	result.DefaultExperienceCount = len(result.DefaultExperiences)
	return result
}

func buildExperienceEventSummary(row postgres.MapExperienceRow) ExperienceEventSummary {
	statusCode, statusText := deriveExperienceEventStatus(row)
	return ExperienceEventSummary{
		EventID:              valueOrEmpty(row.EventPublicID),
		Title:                fallbackText(row.EventDisplayName, "未命名活动"),
		Subtitle:             normalizeOptionalText(row.EventSummary),
		EventType:            deriveExperienceEventType(row),
		Status:               statusText,
		StatusCode:           statusCode,
		CTAText:              deriveExperienceEventCTA(statusCode, row.EventIsDefaultExperience),
		IsDefaultExperience:  row.EventIsDefaultExperience,
		ShowInEventList:      row.EventShowInEventList,
		CurrentPresentation:  buildPresentationSummaryFromMapExperienceRow(row),
		CurrentContentBundle: buildContentBundleSummaryFromMapExperienceRow(row),
	}
}

func deriveExperienceEventStatus(row postgres.MapExperienceRow) (string, string) {
	if row.EventStatus == nil {
		return "pending", "状态待确认"
	}
	switch strings.TrimSpace(*row.EventStatus) {
	case "active":
		if row.EventReleasePayloadJSON == nil || strings.TrimSpace(*row.EventReleasePayloadJSON) == "" {
			return "upcoming", "即将开始"
		}
		if row.EventPresentationID == nil || row.EventContentBundleID == nil {
			return "upcoming", "即将开始"
		}
		return "running", "进行中"
	case "archived", "disabled", "inactive":
		return "ended", "已结束"
	default:
		return "pending", "状态待确认"
	}
}

func deriveExperienceEventCTA(statusCode string, isDefault bool) string {
	if isDefault {
		return "进入体验"
	}
	switch statusCode {
	case "running":
		return "进入活动"
	case "ended":
		return "查看回顾"
	default:
		return "查看详情"
	}
}

func deriveExperienceEventType(row postgres.MapExperienceRow) *string {
	if row.EventReleasePayloadJSON != nil {
		payload, err := decodeJSONObject(*row.EventReleasePayloadJSON)
		if err == nil {
			if game, ok := payload["game"].(map[string]any); ok {
				if rawMode, ok := game["mode"].(string); ok {
					switch strings.TrimSpace(rawMode) {
					case "classic-sequential":
						text := "顺序赛"
						return &text
					case "score-o":
						text := "积分赛"
						return &text
					}
				}
			}
			if plan := resolveVariantPlan(row.EventReleasePayloadJSON); plan.AssignmentMode != nil && *plan.AssignmentMode == AssignmentModeManual {
				text := "多赛道"
				return &text
			}
		}
	}
	if row.EventIsDefaultExperience {
		text := "体验活动"
		return &text
	}
	return nil
}

func buildPresentationSummaryFromMapExperienceRow(row postgres.MapExperienceRow) *PresentationSummaryView {
	if row.EventPresentationID == nil {
		return nil
	}
	summary := &PresentationSummaryView{
		PresentationID:   *row.EventPresentationID,
		Name:             row.EventPresentationName,
		PresentationType: row.EventPresentationType,
	}
	if row.EventPresentationSchema != nil && strings.TrimSpace(*row.EventPresentationSchema) != "" {
		if schema, err := decodeJSONObject(*row.EventPresentationSchema); err == nil {
			summary.TemplateKey = readStringField(schema, "templateKey")
			summary.Version = readStringField(schema, "version")
		}
	}
	return summary
}

func buildContentBundleSummaryFromMapExperienceRow(row postgres.MapExperienceRow) *ContentBundleSummaryView {
	if row.EventContentBundleID == nil {
		return nil
	}
	summary := &ContentBundleSummaryView{
		ContentBundleID: *row.EventContentBundleID,
		Name:            row.EventContentBundleName,
		EntryURL:        row.EventContentEntryURL,
		AssetRootURL:    row.EventContentAssetRootURL,
	}
	if row.EventContentMetadataJSON != nil && strings.TrimSpace(*row.EventContentMetadataJSON) != "" {
		if metadata, err := decodeJSONObject(*row.EventContentMetadataJSON); err == nil {
			summary.BundleType = readStringField(metadata, "bundleType")
			summary.Version = readStringField(metadata, "version")
		}
	}
	return summary
}

func normalizeOptionalText(value *string) *string {
	if value == nil {
		return nil
	}
	trimmed := strings.TrimSpace(*value)
	if trimmed == "" {
		return nil
	}
	return &trimmed
}

func fallbackText(value *string, fallback string) string {
	if value == nil {
		return fallback
	}
	trimmed := strings.TrimSpace(*value)
	if trimmed == "" {
		return fallback
	}
	return trimmed
}

func valueOrEmpty(value *string) string {
	if value == nil {
		return ""
	}
	return *value
}

func containsString(values []string, target string) bool {
	for _, item := range values {
		if item == target {
			return true
		}
	}
	return false
}
