// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

// Package config handles loading and saving lune configuration from TOML.
package config

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/BurntSushi/toml"
)

// ErrNotFound indicates the config file doesn't exist.
var ErrNotFound = errors.New("config file not found")

// Config represents the lune configuration file structure.
type Config struct {
	UI        UIConfig   `toml:"ui"`
	Defaults  Defaults   `toml:"defaults"`
	Areas     []Area     `toml:"areas"`
	Notebooks []Notebook `toml:"notebooks"`
	Habits    []Habit    `toml:"habits"`
}

// UIConfig holds user interface preferences.
type UIConfig struct {
	Color string `toml:"color"` // "always", "never", "auto"
}

// Defaults holds default selections for commands.
type Defaults struct {
	Area     string `toml:"area"`
	Notebook string `toml:"notebook"`
}

// Area represents a Lunatask area of life with its goals.
type Area struct {
	ID    string `toml:"id"`
	Name  string `toml:"name"`
	Key   string `toml:"key"`
	Goals []Goal `toml:"goals"`
}

// Goal represents a goal within an area.
type Goal struct {
	ID   string `toml:"id"`
	Name string `toml:"name"`
	Key  string `toml:"key"`
}

// Notebook represents a Lunatask notebook for notes.
type Notebook struct {
	ID   string `toml:"id"`
	Name string `toml:"name"`
	Key  string `toml:"key"`
}

// Habit represents a trackable habit.
type Habit struct {
	ID   string `toml:"id"`
	Name string `toml:"name"`
	Key  string `toml:"key"`
}

// Path returns the path to the config file.
func Path() (string, error) {
	configDir, err := os.UserConfigDir()
	if err != nil {
		return "", fmt.Errorf("getting config dir: %w", err)
	}

	return filepath.Join(configDir, "lunatask", "config.toml"), nil
}

// Load reads the config file. Returns ErrNotFound if the file doesn't exist.
func Load() (*Config, error) {
	path, err := Path()
	if err != nil {
		return nil, err
	}

	var cfg Config
	if _, err := toml.DecodeFile(path, &cfg); err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil, ErrNotFound
		}

		return nil, fmt.Errorf("decoding config: %w", err)
	}

	return &cfg, nil
}

// Save writes the config to disk.
func (c *Config) Save() error {
	path, err := Path()
	if err != nil {
		return err
	}

	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		return fmt.Errorf("creating config dir: %w", err)
	}

	f, err := os.Create(path) //nolint:gosec // path is from user config dir
	if err != nil {
		return fmt.Errorf("creating config file: %w", err)
	}

	if err := toml.NewEncoder(f).Encode(c); err != nil {
		_ = f.Close()

		return fmt.Errorf("encoding config: %w", err)
	}

	if err := f.Close(); err != nil {
		return fmt.Errorf("closing config file: %w", err)
	}

	return nil
}

// AreaByKey finds an area by its key.
func (c *Config) AreaByKey(key string) *Area {
	for i := range c.Areas {
		if c.Areas[i].Key == key {
			return &c.Areas[i]
		}
	}

	return nil
}

// NotebookByKey finds a notebook by its key.
func (c *Config) NotebookByKey(key string) *Notebook {
	for i := range c.Notebooks {
		if c.Notebooks[i].Key == key {
			return &c.Notebooks[i]
		}
	}

	return nil
}

// HabitByKey finds a habit by its key.
func (c *Config) HabitByKey(key string) *Habit {
	for i := range c.Habits {
		if c.Habits[i].Key == key {
			return &c.Habits[i]
		}
	}

	return nil
}

// GoalByKey finds a goal within this area by its key.
func (a *Area) GoalByKey(key string) *Goal {
	for i := range a.Goals {
		if a.Goals[i].Key == key {
			return &a.Goals[i]
		}
	}

	return nil
}
