package models

import (
	"errors"
	"fmt"
	"time"
)

func GetLocalNode() NodeInfo {
	mu.RLock()
	defer mu.RUnlock()
	return cfg.Node
}

func UpdateLocalNode(n NodeInfo) error {
	mu.Lock()
	defer mu.Unlock()
	if n.Hostname != "" {
		cfg.Node.Hostname = n.Hostname
	}
	if n.Description != "" {
		cfg.Node.Description = n.Description
	}
	if n.ResticPassword != "" {
		cfg.Node.ResticPassword = n.ResticPassword
	}
	return persist()
}

// GetResticPassword returns the node-wide restic repository password used to
// encrypt/decrypt every restic backup target on this node.
func GetResticPassword() string {
	mu.RLock()
	defer mu.RUnlock()
	return cfg.Node.ResticPassword
}

func GetUpdateHistory() []UpdateRecord {
	mu.RLock()
	defer mu.RUnlock()
	result := make([]UpdateRecord, len(cfg.Node.UpdateHistory))
	copy(result, cfg.Node.UpdateHistory)
	return result
}

func GetUpdateRecord(id string) (UpdateRecord, error) {
	mu.RLock()
	defer mu.RUnlock()
	for _, r := range cfg.Node.UpdateHistory {
		if r.ID == id {
			return r, nil
		}
	}
	return UpdateRecord{}, errors.New("update record not found")
}

func AddUpdateRecord(r UpdateRecord) (UpdateRecord, error) {
	mu.Lock()
	defer mu.Unlock()
	r.ID = fmt.Sprintf("upd_%d", time.Now().UnixNano())
	r.Timestamp = time.Now().UTC()
	cfg.Node.UpdateHistory = append(cfg.Node.UpdateHistory, r)
	return r, persist()
}

func DeleteUpdateRecord(id string) error {
	mu.Lock()
	defer mu.Unlock()
	for i, r := range cfg.Node.UpdateHistory {
		if r.ID == id {
			cfg.Node.UpdateHistory = append(cfg.Node.UpdateHistory[:i], cfg.Node.UpdateHistory[i+1:]...)
			return persist()
		}
	}
	return errors.New("update record not found")
}

func GetNodeBackupConfig() BackupConfig {
	mu.RLock()
	defer mu.RUnlock()
	return cfg.Node.Backup
}

func UpdateNodeBackupConfig(bc BackupConfig) error {
	mu.Lock()
	history := cfg.Node.Backup.History
	cfg.Node.Backup = bc
	cfg.Node.Backup.History = history
	err := persist()
	mu.Unlock()
	if err != nil {
		return err
	}
	SyncSchedule()
	return nil
}

func AppendNodeBackupExecution(ex BackupExecution) error {
	mu.Lock()
	defer mu.Unlock()
	cfg.Node.Backup.History = append(cfg.Node.Backup.History, ex)
	now := time.Now().UTC()
	cfg.Node.Backup.LastRun = &now
	for i, t := range cfg.Node.Backup.Targets {
		if t.ID == ex.TargetID {
			cfg.Node.Backup.Targets[i].LastRun = &now
			break
		}
	}
	return persist()
}

// GetNodeBackupTargets lists the node's individual backup target definitions.
func GetNodeBackupTargets() []BackupTarget {
	mu.RLock()
	defer mu.RUnlock()
	result := make([]BackupTarget, len(cfg.Node.Backup.Targets))
	copy(result, cfg.Node.Backup.Targets)
	return result
}

// GetNodeBackupTarget returns a single node backup target by ID.
func GetNodeBackupTarget(tid string) (BackupTarget, error) {
	mu.RLock()
	defer mu.RUnlock()
	for _, t := range cfg.Node.Backup.Targets {
		if t.ID == tid {
			return t, nil
		}
	}
	return BackupTarget{}, errors.New("backup target not found")
}

// AddNodeBackupTarget creates a new backup target definition for the node.
func AddNodeBackupTarget(t BackupTarget) (BackupTarget, error) {
	if err := validateBackupTarget(t); err != nil {
		return BackupTarget{}, err
	}
	mu.Lock()
	t.ID = fmt.Sprintf("bt_%d", time.Now().UnixNano())
	cfg.Node.Backup.Targets = append(cfg.Node.Backup.Targets, t)
	err := persist()
	mu.Unlock()
	if err != nil {
		return BackupTarget{}, err
	}
	SyncSchedule()
	return t, nil
}

// UpdateNodeBackupTarget replaces an existing node backup target definition.
func UpdateNodeBackupTarget(tid string, t BackupTarget) (BackupTarget, error) {
	if err := validateBackupTarget(t); err != nil {
		return BackupTarget{}, err
	}
	mu.Lock()
	found := false
	for i, existing := range cfg.Node.Backup.Targets {
		if existing.ID == tid {
			t.ID = tid
			t.LastRun = existing.LastRun
			cfg.Node.Backup.Targets[i] = t
			found = true
			break
		}
	}
	if !found {
		mu.Unlock()
		return BackupTarget{}, errors.New("backup target not found")
	}
	err := persist()
	mu.Unlock()
	if err != nil {
		return BackupTarget{}, err
	}
	SyncSchedule()
	return t, nil
}

// DeleteNodeBackupTarget removes a node backup target definition.
func DeleteNodeBackupTarget(tid string) error {
	mu.Lock()
	found := false
	for i, t := range cfg.Node.Backup.Targets {
		if t.ID == tid {
			cfg.Node.Backup.Targets = append(cfg.Node.Backup.Targets[:i], cfg.Node.Backup.Targets[i+1:]...)
			found = true
			break
		}
	}
	if !found {
		mu.Unlock()
		return errors.New("backup target not found")
	}
	err := persist()
	mu.Unlock()
	if err != nil {
		return err
	}
	SyncSchedule()
	return nil
}

// RunNodeBackup runs the node's backup for the given target IDs (nil/empty
// means all targets), appending an execution record per target.
func RunNodeBackup(targetIDs []string) error {
	bc := GetNodeBackupConfig()
	targets := filterTargets(bc.Targets, targetIDs)
	if len(targets) == 0 {
		return errors.New("no matching backup targets")
	}
	sourcePath := bc.SourcePath
	if sourcePath == "" {
		sourcePath = ConfigPath()
	}
	results := RunBackup("node", targets, sourcePath)
	for _, ex := range results {
		_ = AppendNodeBackupExecution(ex)
	}
	return nil
}
