package config

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
)

const (
	configDir  = ".investibook"
	configFile = "config.json"
)

type Profile struct {
	API              string `json:"api"`
	Token            string `json:"token"`
	APIKey           string `json:"api_key,omitempty"`
	Email            string `json:"email"`
	DefaultWorkspace string `json:"defaultWorkspace,omitempty"`
}

type Config struct {
	ActiveProfile string             `json:"active_profile,omitempty"`
	Profiles      map[string]Profile `json:"profiles,omitempty"`

	// Legacy flat fields — populated on load if no profiles map exists.
	// Kept for backward compatibility; do not write these on save.
	API              string `json:"api,omitempty"`
	Token            string `json:"token,omitempty"`
	APIKey           string `json:"api_key,omitempty"`
	Email            string `json:"email,omitempty"`
	DefaultWorkspace string `json:"defaultWorkspace,omitempty"`
}

func Dir() string {
	home, err := os.UserHomeDir()
	if err != nil {
		home = "."
	}
	return filepath.Join(home, configDir)
}

func Path() string {
	return filepath.Join(Dir(), configFile)
}

func Load() (*Config, error) {
	cfg := &Config{}

	data, err := os.ReadFile(Path())
	if err == nil {
		_ = json.Unmarshal(data, cfg)
	}

	// Migrate legacy flat config into a "default" profile.
	if cfg.Profiles == nil && cfg.API != "" {
		cfg.Profiles = map[string]Profile{
			"default": {
				API:              cfg.API,
				Token:            cfg.Token,
				APIKey:           cfg.APIKey,
				Email:            cfg.Email,
				DefaultWorkspace: cfg.DefaultWorkspace,
			},
		}
		if cfg.ActiveProfile == "" {
			cfg.ActiveProfile = "default"
		}
	}

	// Env var overrides apply to the active profile at runtime (not persisted).
	if v := os.Getenv("INVESTIBOOK_API"); v != "" {
		p := cfg.ActiveProfileData()
		p.API = v
		cfg.setProfile(cfg.activeProfileName(), p)
	}
	if v := os.Getenv("INVESTIBOOK_TOKEN"); v != "" {
		p := cfg.ActiveProfileData()
		p.Token = v
		cfg.setProfile(cfg.activeProfileName(), p)
	}
	if v := os.Getenv("INVESTIBOOK_WORKSPACE"); v != "" {
		p := cfg.ActiveProfileData()
		p.DefaultWorkspace = v
		cfg.setProfile(cfg.activeProfileName(), p)
	}

	return cfg, nil
}

func Save(cfg *Config) error {
	dir := Dir()
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return fmt.Errorf("create config dir: %w", err)
	}
	// Never write legacy flat fields.
	out := &Config{
		ActiveProfile: cfg.ActiveProfile,
		Profiles:      cfg.Profiles,
	}
	data, err := json.MarshalIndent(out, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(Path(), data, 0o600)
}

// SetActiveProfile switches the active profile (persisted on next Save).
func (c *Config) SetActiveProfile(name string) {
	c.ActiveProfile = name
}

// ActiveProfileName returns the active profile name, defaulting to "default".
func (c *Config) ActiveProfileName() string {
	return c.activeProfileName()
}

// ActiveProfileData returns the Profile for the currently active profile.
// Returns an empty Profile if none is set.
func (c *Config) ActiveProfileData() Profile {
	name := c.activeProfileName()
	if c.Profiles == nil {
		return Profile{}
	}
	return c.Profiles[name]
}

// UpsertProfile creates or updates a named profile.
func (c *Config) UpsertProfile(name string, p Profile) {
	if c.Profiles == nil {
		c.Profiles = make(map[string]Profile)
	}
	c.Profiles[name] = p
	if c.ActiveProfile == "" {
		c.ActiveProfile = name
	}
}

func (c *Config) activeProfileName() string {
	if c.ActiveProfile != "" {
		return c.ActiveProfile
	}
	return "default"
}

func (c *Config) setProfile(name string, p Profile) {
	if c.Profiles == nil {
		c.Profiles = make(map[string]Profile)
	}
	c.Profiles[name] = p
}

// ResolveWorkspace returns the workspace ID to use, checking flag → env → profile default.
func (c *Config) ResolveWorkspace(flagValue string) string {
	if flagValue != "" {
		return flagValue
	}
	if v := os.Getenv("INVESTIBOOK_WORKSPACE"); v != "" {
		return v
	}
	return c.ActiveProfileData().DefaultWorkspace
}
