package ledger

import (
	"encoding/json"
	"strings"

	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	"go.mongodb.org/mongo-driver/v2/bson"
)

// PartyKind (string-backed enum) — readable in BSON/JSON, safe in Go.
type PartyKind string

const (
	PartyKindPerson       PartyKind = "person"
	PartyKindOrganization PartyKind = "organization"
	PartyKindExternal     PartyKind = "external" // not mapped to internal user/org
)

func (k *PartyKind) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	switch PartyKind(s) {
	case PartyKindPerson, PartyKindOrganization, PartyKindExternal:
		*k = PartyKind(s)
		return nil
	default:
		return &ValidationError{Issues: []ValidationIssue{{
			Field: "kind", Code: "invalid_kind", Msg: "expected person|organization|external",
		}}}
	}
}

// Party represents a legal person or organization that can own accounts.
// Composed with your storable.Base and model.PermissionBound.
type Party struct {
	model.PermissionBound `bson:",inline" json:",inline"`

	Kind            PartyKind      `bson:"kind"             json:"kind"`
	Name            string         `bson:"name"             json:"name"`
	UserRef         *bson.ObjectID `bson:"userRef,omitempty"         json:"userRef,omitempty"`         // internal user, if applicable
	OrganizationRef *bson.ObjectID `bson:"organizationRef,omitempty" json:"organizationRef,omitempty"` // internal org, if applicable
	// add your own fields here if needed (KYC flags, etc.)
}

func (p *Party) Collection() string {
	return mservice.LedgerParties
}

func (p *Party) Validate() error {
	var verr *ValidationError

	if strings.TrimSpace(p.Name) == "" {
		veAdd(&verr, "name", "required", "party name is required")
	}
	switch p.Kind {
	case PartyKindPerson:
		if p.OrganizationRef != nil {
			veAdd(&verr, "organizationRef", "must_be_nil", "person party cannot have organizationRef")
		}
	case PartyKindOrganization:
		if p.UserRef != nil {
			veAdd(&verr, "userRef", "must_be_nil", "organization party cannot have userRef")
		}
	case PartyKindExternal:
		if p.UserRef != nil || p.OrganizationRef != nil {
			veAdd(&verr, "refs", "must_be_nil", "external party cannot reference internal user/org")
		}
	default:
		veAdd(&verr, "kind", "invalid", "unknown party kind")
	}
	if verr == nil {
		return nil
	}
	return verr
}
