package ceph

import (
	"io/fs"
	"net/netip"

	"asciigoat.org/ini/basic"
	"asciigoat.org/ini/parser"

	"darvaza.org/core"
)

var sectionMap = map[string]func(*Config, *basic.Section) error{
	"global": loadGlobalConfSection,
}

func loadConfSection(out *Config, src *basic.Section) error {
	h, ok := sectionMap[src.Key]
	if !ok {
		return core.Wrapf(fs.ErrInvalid, "unknown section %q", src.Key)
	}

	return h(out, src)
}

func loadGlobalConfSection(out *Config, src *basic.Section) error {
	var cfg GlobalConfig

	for _, field := range src.Fields {
		if err := loadGlobalConfField(&cfg, field); err != nil {
			return core.Wrap(err, "global")
		}
	}

	out.Global = cfg
	return nil
}

// revive:disable:cyclomatic
// revive:disable:cognitive-complexity

func loadGlobalConfField(cfg *GlobalConfig, field basic.Field) error {
	// revive:enable:cyclomatic
	// revive:enable:cognitive-complexity

	// TODO: refactor when asciigoat's ini parser learns to do reflection

	switch field.Key {
	case "fsid":
		if !core.IsZero(cfg.FSID) {
			return core.Wrapf(fs.ErrInvalid, "duplicate field %q", field.Key)
		}

		err := cfg.FSID.UnmarshalText([]byte(field.Value))
		switch {
		case err != nil:
			return core.Wrap(err, field.Key)
		default:
			return nil
		}
	case "mon_host":
		entries, _ := parser.SplitCommaArray(field.Value)
		for _, s := range entries {
			var addr netip.Addr

			if err := addr.UnmarshalText([]byte(s)); err != nil {
				return core.Wrap(err, field.Key)
			}

			cfg.MonitorsAddr = append(cfg.MonitorsAddr, addr)
		}
		return nil
	case "mon_initial_members":
		entries, _ := parser.SplitCommaArray(field.Value)
		cfg.Monitors = append(cfg.Monitors, entries...)
		return nil
	case "cluster_network":
		if !core.IsZero(cfg.ClusterNetwork) {
			err := core.Wrap(fs.ErrInvalid, "fields before the first section")
			return err
		}

		err := cfg.ClusterNetwork.UnmarshalText([]byte(field.Value))
		switch {
		case err != nil:
			return core.Wrap(err, field.Key)
		default:
			return nil
		}
	}
	return nil
}

func newConfigFromDocument(doc *basic.Document) (*Config, error) {
	var out Config

	if len(doc.Global) > 0 {
		err := core.Wrap(fs.ErrInvalid, "fields before the first section")
		return nil, err
	}

	for i := range doc.Sections {
		src := &doc.Sections[i]
		if err := loadConfSection(&out, src); err != nil {
			return nil, err
		}
	}

	return &out, nil
}