package models

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

func GetAllServices() []Service {
	mu.RLock()
	defer mu.RUnlock()
	result := make([]Service, len(cfg.Services))
	copy(result, cfg.Services)
	return result
}

func GetService(id string) (Service, error) {
	mu.RLock()
	defer mu.RUnlock()
	for _, s := range cfg.Services {
		if s.ID == id {
			return s, nil
		}
	}
	return Service{}, errors.New("service not found")
}

func AddService(s Service) (Service, error) {
	mu.Lock()
	s.ID = fmt.Sprintf("svc_%d", time.Now().UnixNano())
	if s.Backup != nil {
		if s.Backup.Targets == nil {
			s.Backup.Targets = []BackupTarget{}
		}
		if s.Backup.History == nil {
			s.Backup.History = []BackupExecution{}
		}
	}
	cfg.Services = append(cfg.Services, s)
	err := persist()
	mu.Unlock()
	if err != nil {
		return Service{}, err
	}
	SyncSchedule()
	return s, nil
}

func UpdateService(id string, s Service) (Service, error) {
	mu.Lock()
	found := false
	for i, svc := range cfg.Services {
		if svc.ID == id {
			s.ID = id
			if s.Backup != nil && svc.Backup != nil {
				s.Backup.History = svc.Backup.History
			}
			cfg.Services[i] = s
			found = true
			break
		}
	}
	if !found {
		mu.Unlock()
		return Service{}, errors.New("service not found")
	}
	err := persist()
	mu.Unlock()
	if err != nil {
		return Service{}, err
	}
	SyncSchedule()
	return s, nil
}

func DeleteService(id string) error {
	mu.Lock()
	found := false
	for i, s := range cfg.Services {
		if s.ID == id {
			cfg.Services = append(cfg.Services[:i], cfg.Services[i+1:]...)
			found = true
			break
		}
	}
	if !found {
		mu.Unlock()
		return errors.New("service not found")
	}
	err := persist()
	mu.Unlock()
	if err != nil {
		return err
	}
	SyncSchedule()
	return nil
}

func UpdateServiceStatus(id, status string) error {
	mu.Lock()
	defer mu.Unlock()
	for i, s := range cfg.Services {
		if s.ID == id {
			cfg.Services[i].Status = status
			return persist()
		}
	}
	return errors.New("service not found")
}

func AppendServiceBackupExecution(id string, ex BackupExecution) error {
	mu.Lock()
	defer mu.Unlock()
	for i, s := range cfg.Services {
		if s.ID == id {
			if cfg.Services[i].Backup == nil {
				cfg.Services[i].Backup = &BackupConfig{
					Targets: []BackupTarget{},
					History: []BackupExecution{},
				}
			}
			cfg.Services[i].Backup.History = append(cfg.Services[i].Backup.History, ex)
			now := time.Now().UTC()
			cfg.Services[i].Backup.LastRun = &now
			for j, t := range cfg.Services[i].Backup.Targets {
				if t.ID == ex.TargetID {
					cfg.Services[i].Backup.Targets[j].LastRun = &now
					break
				}
			}
			return persist()
		}
	}
	return errors.New("service not found")
}

// GetServiceBackupTargets lists a service's individual backup target definitions.
func GetServiceBackupTargets(id string) ([]BackupTarget, error) {
	mu.RLock()
	defer mu.RUnlock()
	for _, s := range cfg.Services {
		if s.ID == id {
			if s.Backup == nil {
				return []BackupTarget{}, nil
			}
			result := make([]BackupTarget, len(s.Backup.Targets))
			copy(result, s.Backup.Targets)
			return result, nil
		}
	}
	return nil, errors.New("service not found")
}

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

// AddServiceBackupTarget creates a new backup target definition for a service.
func AddServiceBackupTarget(id string, t BackupTarget) (BackupTarget, error) {
	if err := validateBackupTarget(t); err != nil {
		return BackupTarget{}, err
	}
	mu.Lock()
	idx := -1
	for i, s := range cfg.Services {
		if s.ID == id {
			idx = i
			break
		}
	}
	if idx == -1 {
		mu.Unlock()
		return BackupTarget{}, errors.New("service not found")
	}
	if cfg.Services[idx].Backup == nil {
		cfg.Services[idx].Backup = &BackupConfig{
			Targets: []BackupTarget{},
			History: []BackupExecution{},
		}
	}
	t.ID = fmt.Sprintf("bt_%d", time.Now().UnixNano())
	cfg.Services[idx].Backup.Targets = append(cfg.Services[idx].Backup.Targets, t)
	err := persist()
	mu.Unlock()
	if err != nil {
		return BackupTarget{}, err
	}
	SyncSchedule()
	return t, nil
}

// UpdateServiceBackupTarget replaces an existing backup target definition for a service.
func UpdateServiceBackupTarget(id, tid string, t BackupTarget) (BackupTarget, error) {
	if err := validateBackupTarget(t); err != nil {
		return BackupTarget{}, err
	}
	mu.Lock()
	svcIdx := -1
	for i, s := range cfg.Services {
		if s.ID == id {
			svcIdx = i
			break
		}
	}
	if svcIdx == -1 || cfg.Services[svcIdx].Backup == nil {
		mu.Unlock()
		return BackupTarget{}, errors.New("service not found")
	}
	found := false
	for i, existing := range cfg.Services[svcIdx].Backup.Targets {
		if existing.ID == tid {
			t.ID = tid
			t.LastRun = existing.LastRun
			cfg.Services[svcIdx].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
}

// DeleteServiceBackupTarget removes a backup target definition from a service.
func DeleteServiceBackupTarget(id, tid string) error {
	mu.Lock()
	svcIdx := -1
	for i, s := range cfg.Services {
		if s.ID == id {
			svcIdx = i
			break
		}
	}
	if svcIdx == -1 || cfg.Services[svcIdx].Backup == nil {
		mu.Unlock()
		return errors.New("service not found")
	}
	found := false
	for i, t := range cfg.Services[svcIdx].Backup.Targets {
		if t.ID == tid {
			cfg.Services[svcIdx].Backup.Targets = append(
				cfg.Services[svcIdx].Backup.Targets[:i],
				cfg.Services[svcIdx].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
}

// RunServiceBackup runs a service's backup for the given target IDs (nil/empty
// means all targets), honoring StopOnBackup, and appends an execution record
// per target.
func RunServiceBackup(id string, targetIDs []string) error {
	svc, err := GetService(id)
	if err != nil {
		return err
	}
	if svc.Backup == nil || len(svc.Backup.Targets) == 0 {
		return errors.New("no backup targets configured for this service")
	}
	targets := filterTargets(svc.Backup.Targets, targetIDs)
	if len(targets) == 0 {
		return errors.New("no matching backup targets")
	}
	sourcePath := svc.BackupFolder
	if svc.Backup.SourcePath != "" {
		sourcePath = svc.Backup.SourcePath
	}
	if svc.StopOnBackup {
		_ = StopService(svc)
	}
	results := RunBackup("svc:"+id, targets, sourcePath)
	for _, ex := range results {
		_ = AppendServiceBackupExecution(id, ex)
	}
	if svc.StopOnBackup {
		_ = StartService(svc)
	}
	return nil
}
