package zap

import (
	oid "git.perx.ru/perxis/perxis-go/id"
	"git.perx.ru/perxis/perxis-go/log"
	"git.perx.ru/perxis/perxis-go/pkg/id"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// WriteSyncer отвечает за хранение и синхронизацию log.Entry
type WriteSyncer interface {
	Write(entry *log.Entry) error
	Sync() error
}

// Core кодирует zapcore.Entry в log.Entry и отправляет их в WriteSyncer
type Core struct {
	zapcore.LevelEnabler

	writeSyncer WriteSyncer
	fields      []zap.Field
}

func NewCore(writeSyncer WriteSyncer) *Core {
	return &Core{
		LevelEnabler: zapcore.InfoLevel,
		writeSyncer:  writeSyncer,
	}
}

func (core *Core) With(fields []zapcore.Field) zapcore.Core {
	return &Core{
		LevelEnabler: core.LevelEnabler,
		writeSyncer:  core.writeSyncer,
		fields:       append(core.fields, fields...),
	}
}

func (core *Core) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
	if core.Enabled(entry.Level) {
		return checkedEntry.AddCore(entry, core)
	}
	return checkedEntry
}

func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
	return core.writeSyncer.Write(core.getEntry(entry, fields))
}

func (core *Core) Sync() error {
	return core.writeSyncer.Sync()
}

func (core *Core) getEntry(entry zapcore.Entry, fields []zapcore.Field) *log.Entry {
	if len(core.fields) > 0 {
		fields = append(fields, core.fields...)
	}

	enc := zapcore.NewMapObjectEncoder()
	for _, field := range fields {
		field.AddTo(enc)
	}

	ent := &log.Entry{
		ID:        id.GenerateNewID(),
		Timestamp: entry.Time,
		LogLevel:  log.Level(entry.Level),
		Message:   entry.Message,
	}

	ent.Category, _ = enc.Fields["category"].(string)
	ent.Component, _ = enc.Fields["component"].(string)
	ent.Event, _ = enc.Fields["event"].(string)
	ent.ObjectID, _ = enc.Fields["object"].(*oid.ObjectId)
	ent.CallerID, _ = enc.Fields["caller"].(*oid.ObjectId)
	ent.Attr = enc.Fields["attr"]

	if tags, ok := enc.Fields["tags"].([]any); ok {
		for _, item := range tags {
			if tag, ok := item.(string); ok {
				ent.Tags = append(ent.Tags, tag)
			}
		}
	}

	return ent
}