package profile

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/dbaas"
)

// databasesFile is the top-level YAML wrapper for databases.yaml.
type databasesFile struct {
	Databases []dbaas.Database `yaml:"databases"`
}

// DatabaseManager provides CRUD operations for database instance configurations.
type DatabaseManager struct {
	mgr *Manager
}

// Get returns the database with the given ID.
func (dm *DatabaseManager) Get(id string) (dbaas.Database, bool) {
	dm.mgr.mu.RLock()
	defer dm.mgr.mu.RUnlock()
	d, ok := dm.mgr.databases[id]
	if !ok {
		return dbaas.Database{}, false
	}
	return *d, true
}

// All returns all databases, sorted by ID.
func (dm *DatabaseManager) All() []dbaas.Database {
	dm.mgr.mu.RLock()
	defer dm.mgr.mu.RUnlock()

	result := make([]dbaas.Database, 0, len(dm.mgr.databases))
	for _, d := range dm.mgr.databases {
		if strings.TrimSpace(d.ID) == "" {
			continue
		}
		result = append(result, *d)
	}
	sort.Slice(result, func(i, j int) bool {
		return strings.TrimSpace(result[i].ID) < strings.TrimSpace(result[j].ID)
	})
	return result
}

// Add adds a new database instance configuration.
func (dm *DatabaseManager) Add(d dbaas.Database) error {
	if strings.TrimSpace(d.ID) == "" {
		d.ID = generateDatabaseID()
	}
	if err := dbaas.ValidateDatabase(d); err != nil {
		return err
	}
	normalized := dbaas.NormalizeDatabase(d)
	dm.mgr.mu.Lock()
	defer dm.mgr.mu.Unlock()
	if _, ok := dm.mgr.databases[normalized.ID]; ok {
		return fmt.Errorf("database %q already exists", normalized.ID)
	}
	dm.mgr.databases[normalized.ID] = &normalized
	return dm.saveDatabasesLocked()
}

// Update updates an existing database instance configuration.
func (dm *DatabaseManager) Update(d dbaas.Database) error {
	if err := dbaas.ValidateDatabase(d); err != nil {
		return err
	}
	normalized := dbaas.NormalizeDatabase(d)
	dm.mgr.mu.Lock()
	defer dm.mgr.mu.Unlock()
	if _, ok := dm.mgr.databases[normalized.ID]; !ok {
		return fmt.Errorf("database %q not found", normalized.ID)
	}
	dm.mgr.databases[normalized.ID] = &normalized
	return dm.saveDatabasesLocked()
}

// Delete removes a database by ID.
func (dm *DatabaseManager) Delete(id string) error {
	dm.mgr.mu.Lock()
	defer dm.mgr.mu.Unlock()
	if _, ok := dm.mgr.databases[id]; !ok {
		return fmt.Errorf("database %q not found", id)
	}
	delete(dm.mgr.databases, id)
	return dm.saveDatabasesLocked()
}

func (dm *DatabaseManager) saveDatabasesLocked() error {
	return saveDatabasesLocked(dm.mgr)
}

// saveDatabasesLocked persists the Manager's databases map to databases.yaml.
// The caller must hold m.mu.
func saveDatabasesLocked(m *Manager) error {
	f := databasesFile{Databases: make([]dbaas.Database, 0, len(m.databases))}
	for _, d := range m.databases {
		f.Databases = append(f.Databases, *d)
	}
	return saveYAMLFile(f, m.filePath("databases.yaml"), m)
}

// reloadDatabases loads databases.yaml into the Manager's databases map.
func reloadDatabases(m *Manager) {
	f := &databasesFile{}
	if err := loadYAMLFile(m.filePath("databases.yaml"), f); err != nil {
		log.Printf("profile: load databases: %v", err)
	}
	databases := make(map[string]*dbaas.Database, len(f.Databases))
	for i := range f.Databases {
		d := f.Databases[i]
		databases[d.ID] = &d
	}
	m.databases = databases
}
