package service

import "strings"

type MapPreviewView struct {
	Mode              string               `json:"mode"`
	BaseTiles         *PreviewBaseTiles    `json:"baseTiles,omitempty"`
	Viewport          *PreviewViewport     `json:"viewport,omitempty"`
	Variants          []PreviewVariantView `json:"variants,omitempty"`
	SelectedVariantID *string              `json:"selectedVariantId,omitempty"`
}

type PreviewBaseTiles struct {
	TileBaseURL string `json:"tileBaseUrl"`
	Zoom        *int   `json:"zoom,omitempty"`
	TileSize    *int   `json:"tileSize,omitempty"`
}

type PreviewViewport struct {
	Width  *int     `json:"width,omitempty"`
	Height *int     `json:"height,omitempty"`
	MinLon *float64 `json:"minLon,omitempty"`
	MinLat *float64 `json:"minLat,omitempty"`
	MaxLon *float64 `json:"maxLon,omitempty"`
	MaxLat *float64 `json:"maxLat,omitempty"`
}

type PreviewVariantView struct {
	VariantID string               `json:"variantId"`
	Name      *string              `json:"name,omitempty"`
	RouteCode *string              `json:"routeCode,omitempty"`
	Controls  []PreviewControlView `json:"controls,omitempty"`
	Legs      []PreviewLegView     `json:"legs,omitempty"`
}

type PreviewControlView struct {
	ID    string   `json:"id"`
	Kind  *string  `json:"kind,omitempty"`
	Lon   *float64 `json:"lon,omitempty"`
	Lat   *float64 `json:"lat,omitempty"`
	Label *string  `json:"label,omitempty"`
}

type PreviewLegView struct {
	From string `json:"from"`
	To   string `json:"to"`
}

func buildPreviewFromPayload(payloadJSON *string) (*MapPreviewView, error) {
	if payloadJSON == nil || strings.TrimSpace(*payloadJSON) == "" {
		return nil, nil
	}

	payload, err := decodeJSONObject(*payloadJSON)
	if err != nil {
		return nil, err
	}
	rawPreview, _ := payload["preview"].(map[string]any)
	if len(rawPreview) == 0 {
		return nil, nil
	}

	view := &MapPreviewView{}
	if mode := readStringField(rawPreview, "mode"); mode != nil {
		view.Mode = *mode
	}
	if view.Mode == "" {
		view.Mode = "readonly"
	}

	if rawBaseTiles, ok := rawPreview["baseTiles"].(map[string]any); ok && len(rawBaseTiles) > 0 {
		baseTiles := &PreviewBaseTiles{}
		if tileBaseURL := readStringField(rawBaseTiles, "tileBaseUrl"); tileBaseURL != nil {
			baseTiles.TileBaseURL = *tileBaseURL
		}
		baseTiles.Zoom = readIntField(rawBaseTiles, "zoom")
		baseTiles.TileSize = readIntField(rawBaseTiles, "tileSize")
		if strings.TrimSpace(baseTiles.TileBaseURL) != "" {
			view.BaseTiles = baseTiles
		}
	}

	if rawViewport, ok := rawPreview["viewport"].(map[string]any); ok && len(rawViewport) > 0 {
		viewport := &PreviewViewport{
			Width:  readIntField(rawViewport, "width"),
			Height: readIntField(rawViewport, "height"),
			MinLon: readFloatField(rawViewport, "minLon"),
			MinLat: readFloatField(rawViewport, "minLat"),
			MaxLon: readFloatField(rawViewport, "maxLon"),
			MaxLat: readFloatField(rawViewport, "maxLat"),
		}
		view.Viewport = viewport
	}

	if selectedVariantID := readStringField(rawPreview, "selectedVariantId"); selectedVariantID != nil {
		view.SelectedVariantID = selectedVariantID
	}

	rawVariants, _ := rawPreview["variants"].([]any)
	if len(rawVariants) > 0 {
		view.Variants = make([]PreviewVariantView, 0, len(rawVariants))
		for _, raw := range rawVariants {
			item, ok := raw.(map[string]any)
			if !ok {
				continue
			}
			variantID := readStringField(item, "variantId")
			if variantID == nil || strings.TrimSpace(*variantID) == "" {
				variantID = readStringField(item, "id")
			}
			if variantID == nil || strings.TrimSpace(*variantID) == "" {
				continue
			}
			variant := PreviewVariantView{
				VariantID: *variantID,
				Name:      readStringField(item, "name"),
				RouteCode: readStringField(item, "routeCode"),
			}
			rawControls, _ := item["controls"].([]any)
			if len(rawControls) > 0 {
				variant.Controls = make([]PreviewControlView, 0, len(rawControls))
				for _, rawControl := range rawControls {
					controlMap, ok := rawControl.(map[string]any)
					if !ok {
						continue
					}
					controlID := readStringField(controlMap, "id")
					if controlID == nil || strings.TrimSpace(*controlID) == "" {
						continue
					}
					variant.Controls = append(variant.Controls, PreviewControlView{
						ID:    *controlID,
						Kind:  readStringField(controlMap, "kind"),
						Lon:   readFloatField(controlMap, "lon"),
						Lat:   readFloatField(controlMap, "lat"),
						Label: readStringField(controlMap, "label"),
					})
				}
			}
			rawLegs, _ := item["legs"].([]any)
			if len(rawLegs) > 0 {
				variant.Legs = make([]PreviewLegView, 0, len(rawLegs))
				for _, rawLeg := range rawLegs {
					legMap, ok := rawLeg.(map[string]any)
					if !ok {
						continue
					}
					from := readStringField(legMap, "from")
					to := readStringField(legMap, "to")
					if from == nil || to == nil || strings.TrimSpace(*from) == "" || strings.TrimSpace(*to) == "" {
						continue
					}
					variant.Legs = append(variant.Legs, PreviewLegView{
						From: *from,
						To:   *to,
					})
				}
			}
			view.Variants = append(view.Variants, variant)
		}
	}

	if view.BaseTiles == nil && view.Viewport == nil && len(view.Variants) == 0 {
		return nil, nil
	}
	return view, nil
}

func readIntField(object map[string]any, key string) *int {
	if object == nil {
		return nil
	}
	value, ok := object[key]
	if !ok {
		return nil
	}
	switch v := value.(type) {
	case int:
		result := v
		return &result
	case int32:
		result := int(v)
		return &result
	case int64:
		result := int(v)
		return &result
	case float64:
		result := int(v)
		return &result
	default:
		return nil
	}
}

func readFloatField(object map[string]any, key string) *float64 {
	if object == nil {
		return nil
	}
	value, ok := object[key]
	if !ok {
		return nil
	}
	switch v := value.(type) {
	case float64:
		result := v
		return &result
	case float32:
		result := float64(v)
		return &result
	case int:
		result := float64(v)
		return &result
	case int32:
		result := float64(v)
		return &result
	case int64:
		result := float64(v)
		return &result
	default:
		return nil
	}
}
