package sub2api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
)

func decodeFlexibleID(raw json.RawMessage) (string, error) {
	if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
		return "", nil
	}

	var asString string
	if err := json.Unmarshal(raw, &asString); err == nil {
		return asString, nil
	}

	decoder := json.NewDecoder(bytes.NewReader(raw))
	decoder.UseNumber()
	var asNumber json.Number
	if err := decoder.Decode(&asNumber); err == nil {
		return asNumber.String(), nil
	}

	return "", fmt.Errorf("unsupported id payload: %s", string(raw))
}

func flexibleIDValue(raw string) any {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return ""
	}
	if id, err := strconv.ParseInt(trimmed, 10, 64); err == nil {
		return id
	}
	return trimmed
}

func flexibleIDSliceValues(raw []string) []any {
	values := make([]any, 0, len(raw))
	for _, item := range raw {
		values = append(values, flexibleIDValue(item))
	}
	return values
}

func (r CreateChannelRequest) MarshalJSON() ([]byte, error) {
	modelMapping := map[string]map[string]string{}
	platform := strings.TrimSpace(r.Platform)
	if platform == "" {
		platform = "openai"
	}
	if len(r.ModelMapping) > 0 {
		inner := make(map[string]string, len(r.ModelMapping))
		for key, value := range r.ModelMapping {
			inner[key] = value
		}
		modelMapping[platform] = inner
	}
	modelPricing := make([]ChannelModelPricing, 0, len(r.ModelPricing))
	for _, entry := range r.ModelPricing {
		pricing := entry
		if strings.TrimSpace(pricing.Platform) == "" {
			pricing.Platform = platform
		}
		modelPricing = append(modelPricing, pricing)
	}

	return json.Marshal(struct {
		Name               string                       `json:"name"`
		GroupIDs           []any                        `json:"group_ids"`
		ModelMapping       map[string]map[string]string `json:"model_mapping,omitempty"`
		ModelPricing       []ChannelModelPricing        `json:"model_pricing,omitempty"`
		RestrictModels     bool                         `json:"restrict_models,omitempty"`
		BillingModelSource string                       `json:"billing_model_source,omitempty"`
	}{
		Name:               r.Name,
		GroupIDs:           flexibleIDSliceValues(r.GroupIDs),
		ModelMapping:       modelMapping,
		ModelPricing:       modelPricing,
		RestrictModels:     r.RestrictModels,
		BillingModelSource: r.BillingModelSource,
	})
}

func (r CreatePlanRequest) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		GroupID      any     `json:"group_id"`
		Name         string  `json:"name"`
		Price        float64 `json:"price"`
		ValidityDays int     `json:"validity_days"`
		ValidityUnit string  `json:"validity_unit"`
	}{
		GroupID:      flexibleIDValue(r.GroupID),
		Name:         r.Name,
		Price:        r.Price,
		ValidityDays: r.ValidityDays,
		ValidityUnit: r.ValidityUnit,
	})
}

func (r CreateAccountRequest) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Name        string         `json:"name"`
		Platform    string         `json:"platform"`
		Type        string         `json:"type"`
		Credentials map[string]any `json:"credentials"`
		GroupIDs    []any          `json:"group_ids"`
	}{
		Name:        r.Name,
		Platform:    r.Platform,
		Type:        r.Type,
		Credentials: r.Credentials,
		GroupIDs:    flexibleIDSliceValues(r.GroupIDs),
	})
}

func (r AssignSubscriptionRequest) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		UserID       any `json:"user_id"`
		GroupID      any `json:"group_id"`
		DurationDays int `json:"validity_days,omitempty"`
	}{
		UserID:       flexibleIDValue(r.UserID),
		GroupID:      flexibleIDValue(r.GroupID),
		DurationDays: r.DurationDays,
	})
}

func (r *NamedResource) UnmarshalJSON(data []byte) error {
	var aux struct {
		ID   json.RawMessage `json:"id"`
		Name string          `json:"name"`
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	id, err := decodeFlexibleID(aux.ID)
	if err != nil {
		return err
	}
	r.ID = id
	r.Name = aux.Name
	return nil
}

func (r *GroupRef) UnmarshalJSON(data []byte) error {
	var aux struct {
		ID   json.RawMessage `json:"id"`
		Name string          `json:"name"`
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	id, err := decodeFlexibleID(aux.ID)
	if err != nil {
		return err
	}
	r.ID = id
	r.Name = aux.Name
	return nil
}

func (r *ChannelRef) UnmarshalJSON(data []byte) error {
	var aux struct {
		ID   json.RawMessage `json:"id"`
		Name string          `json:"name"`
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	id, err := decodeFlexibleID(aux.ID)
	if err != nil {
		return err
	}
	r.ID = id
	r.Name = aux.Name
	return nil
}

func (r *PlanRef) UnmarshalJSON(data []byte) error {
	var aux struct {
		ID   json.RawMessage `json:"id"`
		Name string          `json:"name"`
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	id, err := decodeFlexibleID(aux.ID)
	if err != nil {
		return err
	}
	r.ID = id
	r.Name = aux.Name
	return nil
}

func (r *AccountRef) UnmarshalJSON(data []byte) error {
	var aux struct {
		ID       json.RawMessage `json:"id"`
		Name     string          `json:"name,omitempty"`
		Platform string          `json:"platform,omitempty"`
		Type     string          `json:"type,omitempty"`
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	id, err := decodeFlexibleID(aux.ID)
	if err != nil {
		return err
	}
	r.ID = id
	r.Name = aux.Name
	r.Platform = aux.Platform
	r.Type = aux.Type
	return nil
}

func (r *SubscriptionRef) UnmarshalJSON(data []byte) error {
	var aux struct {
		ID json.RawMessage `json:"id"`
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	id, err := decodeFlexibleID(aux.ID)
	if err != nil {
		return err
	}
	r.ID = id
	return nil
}
