package svc

import (
	"fmt"
	"time"

	"git.simanc.com/software/golib/v4/features/mo"
	"git.simanc.com/software/golib/v4/infra/ii"
)

type Row struct {
	mo.D
}

//goland:noinspection ALL
func (c *Row) Clone() Row {
	r := make(mo.D, len(c.D))
	for i, v := range c.D {
		r[i] = v
	}
	return Row{D: r}
}

//goland:noinspection ALL
func (c *Row) ID() mo.ObjectID {
	return c.ObjectID(mo.OID)
}

//goland:noinspection ALL
func (c *Row) Any(k string) any {
	v, _ := c.Get(k)
	return v
}

//goland:noinspection ALL
func (c *Row) Double(k string) float64 {
	v, ok := c.Get(k)
	if !ok {
		return 0
	}
	return v.(float64)
}

//goland:noinspection ALL
func (c *Row) Strings(k string) string {
	v, ok := c.Get(k)
	if !ok {
		return ""
	}
	return v.(string)
}

//goland:noinspection ALL
func (c *Row) Object(k string) Row {
	v, ok := c.Get(k)
	if !ok {
		return Row{}
	}
	return Row{D: v.(mo.D)}
}

//goland:noinspection ALL
func (c *Row) ObjectTo(k string, val any) error {
	v, ok := c.Get(k)
	if !ok {
		return fmt.Errorf("field %v not found in Row", k)
	}
	return mo.Decode(v, val)
}

//goland:noinspection ALL
func (c *Row) Array(k string) mo.A {
	v, ok := c.Get(k)
	if !ok {
		return mo.A{}
	}
	return v.(mo.A)
}

//goland:noinspection ALL
func (c *Row) Binary(k string) mo.Binary {
	v, ok := c.Get(k)
	if !ok {
		return mo.Binary{}
	}
	return v.(mo.Binary)
}

//goland:noinspection ALL
func (c *Row) ObjectID(k string) mo.ObjectID {
	v, ok := c.Get(k)
	if !ok {
		return mo.ObjectID{}
	}
	return v.(mo.ObjectID)
}

//goland:noinspection ALL
func (c *Row) Boolean(k string) bool {
	v, ok := c.Get(k)
	if !ok {
		return false
	}
	return v.(bool)
}

//goland:noinspection ALL
func (c *Row) Date(k string) mo.DateTime {
	v, ok := c.Get(k)
	if !ok {
		return mo.DateTime(0)
	}
	return v.(mo.DateTime)
}

//goland:noinspection ALL
func (c *Row) Int32(k string) int32 {
	v, ok := c.Get(k)
	if !ok {
		return 0
	}
	return v.(int32)
}

//goland:noinspection ALL
func (c *Row) Int64(k string) int64 {
	v, ok := c.Get(k)
	if !ok {
		return 0
	}
	return v.(int64)
}

//goland:noinspection ALL
func (c *Row) Has(k string) bool {
	v, ok := c.Get(k)
	if !ok {
		return false
	}
	return v != nil
}

//goland:noinspection ALL
func (c *Row) Range(f func(i int, e mo.E) bool) {
	for i, e := range c.D {
		if !f(i, e) {
			return
		}
	}
}

//goland:noinspection ALL
func (c *Row) ToM() mo.M {
	r := make(mo.M, len(c.D))
	c.Range(func(i int, e mo.E) bool {
		r[e.Key] = e.Value
		return true
	})
	return r
}

//goland:noinspection ALL
func (c *Row) CopyToSet(updater *mo.Updater) {
	c.Range(func(_ int, e mo.E) bool {
		updater.Set(e.Key, e.Value)
		return true
	})
}

//goland:noinspection ALL
func (c *Row) Get(k string) (any, bool) {
	for _, e := range c.D {
		if e.Key == k {
			return e.Value, true
		}
	}
	return nil, false
}

//goland:noinspection ALL
func (c *Row) Add(k string, v any) {
	c.D = append(c.D, mo.E{Key: k, Value: v})
}

//goland:noinspection ALL
func (c *Row) Set(k string, v any) {
	set := false
	c.Range(func(i int, e mo.E) bool {
		if e.Key == k {
			c.D[i].Value = v
			set = true
			return false
		}
		return true
	})
	if !set {
		c.Add(k, v)
	}
}

//goland:noinspection ALL
func (c *Row) Del(k string) {
	for i, e := range c.D {
		if e.Key == k {
			c.D = append(c.D[:i], c.D[i+1:]...)
		}
	}
}

//goland:noinspection ALL
func (c *Row) CreationTime() time.Time {
	if creat := c.Date(ii.CreationTime); creat > 0 {
		return creat.Time().Local()
	}
	return time.Time{}
}

//goland:noinspection ALL
func (c *Row) LastModified() time.Time {
	if last := c.Date(ii.LastModified); last > 0 {
		return last.Time().Local()
	}
	return c.CreationTime()
}

//goland:noinspection ALL
func (c Row) MarshalJSON() ([]byte, error) {
	row := c.Clone()
	if row.Has(ii.CreationTime) {
		row.Set(ii.CreationTime, c.CreationTime().Format(time.DateTime))
	}
	if row.Has(ii.LastModified) {
		row.Set(ii.LastModified, c.LastModified().Format(time.DateTime))
	}
	return row.D.MarshalJSON()
}

//goland:noinspection ALL
func (c *Row) UnmarshalBSON(data []byte) error {
	return mo.Unmarshal(data, &c.D)
}

//goland:noinspection ALL
func (c Row) MarshalBSON() ([]byte, error) {
	return mo.Marshal(c.D)
}
