package entity

import (
	"encoding/json"
	"fmt"
	"time"

	"go.mongodb.org/mongo-driver/bson"
)

const (
	Name         = "_id"
	JSONName     = "name"
	Relationship = "relationship"
	Type         = "type"
	Title        = "title"
	Text         = "text"
	Modified     = "modified"
	Connections  = "connections"
	Attachments  = "attachments"
)

type One struct {
	Name         string            `bson:"_id,omitempty" json:"name,omitempty"`
	Type         string            `bson:"type,omitempty" json:"type,omitempty"`
	Title        string            `bson:"title,omitempty" json:"title,omitempty"`
	Text         string            `bson:"text,omitempty" json:"text,omitempty"`
	Relationship string            `bson:"relationship,omitempty" json:"relationship,omitempty"`
	Modified     int64             `bson:"modified,omitempty" json:"modified,omitempty"`
	Connections  map[string]One    `bson:"connections,omitempty" json:"connections,omitempty"`
	Attachments  map[string]string `bson:"attachments,omitempty" json:"attachments,omitempty"`
}

func (o One) Query() One {
	return One{Name: o.Name}
}

func (o One) Peer() One {
	return One{
		Name:         o.Name,
		Type:         o.Type,
		Title:        o.Title,
		Relationship: o.Relationship,
		Modified:     o.Modified,
	}
}

func (o One) Peers() []string {
	names := make([]string, len(o.Connections))
	i := 0
	for k := range o.Connections {
		names[i] = o.Connections[k].Name
		i += 1
	}
	return names
}

func (o One) MarshalBSON() ([]byte, error) {
	isMin := fmt.Sprint(o) == fmt.Sprint(o.Query())
	if !isMin {
		o.Modified = time.Now().UnixNano()
	}
	b, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	m := bson.M{}
	if err := json.Unmarshal(b, &m); err != nil {
		return nil, err
	}
	if name, ok := m[JSONName]; ok {
		m[Name] = name
		delete(m, JSONName)
	}
	if !isMin {
		connections := map[string]interface{}{}
		switch m[Connections].(type) {
		case nil:
		case map[string]interface{}:
			connections = m[Connections].(map[string]interface{})
		default:
			return nil, fmt.Errorf("bad connections type %T", m[Connections])
		}
		for k := range connections {
			b, err := bson.Marshal(o.Connections[k])
			if err != nil {
				return nil, err
			}
			m := bson.M{}
			if err := bson.Unmarshal(b, &m); err != nil {
				return nil, err
			}
			connections[k] = m
		}
		m[Connections] = connections
	}
	return bson.Marshal(m)
}
