package model

import (
	"strings"
	"time"
)

const SchemaVersion = 1

type JobType string

const (
	JobShare   JobType = "share"
	JobAppdata JobType = "appdata"
	JobDocker  JobType = "docker"
	JobVM      JobType = "vm"
	JobFlash   JobType = "flash"
	JobRsync   JobType = "rsync"
)

type RepositoryType string

const (
	RepositoryLocal RepositoryType = "local"
	RepositorySMB   RepositoryType = "smb"
	RepositoryNFS   RepositoryType = "nfs"
	RepositorySFTP  RepositoryType = "sftp"
)

type Config struct {
	SchemaVersion int                  `json:"schemaVersion"`
	Jobs          []Job                `json:"jobs"`
	Repositories  []Repository         `json:"repositories"`
	Notifications []NotificationTarget `json:"notifications"`
	Settings      Settings             `json:"settings"`
}

type Settings struct {
	ResticPath       string `json:"resticPath"`
	RsyncPath        string `json:"rsyncPath"`
	RestoreRoot      string `json:"restoreRoot"`
	PersistentLogDir string `json:"persistentLogDir,omitempty"`
	ShowLiveLog      bool   `json:"showLiveLog"`
	CatchUpWindowHrs int    `json:"catchUpWindowHours"`
	CheckCron        string `json:"checkCron"`
	PruneCron        string `json:"pruneCron"`
}

type Job struct {
	SchemaVersion int          `json:"schemaVersion"`
	ID            string       `json:"id"`
	Name          string       `json:"name"`
	Type          JobType      `json:"type"`
	Enabled       bool         `json:"enabled"`
	RepositoryID  string       `json:"repositoryId"`
	Sources       []Source     `json:"sources"`
	Schedule      Schedule     `json:"schedule"`
	Consistency   Consistency  `json:"consistency"`
	Compression   string       `json:"compression"`
	CPUCores      int          `json:"cpuCores,omitempty"`
	Excludes      []string     `json:"excludes,omitempty"`
	Tags          []string     `json:"tags,omitempty"`
	FlashImage    bool         `json:"flashImage,omitempty"`
	Retention     Retention    `json:"retention"`
	NotifyOn      []string     `json:"notifyOn,omitempty"`
	ShutdownSecs  int          `json:"shutdownTimeoutSeconds"`
	Rsync         RsyncOptions `json:"rsync,omitempty"`
}

type RsyncOptions struct {
	Target              string `json:"target"`
	Overwrite           bool   `json:"overwrite"`
	Delete              bool   `json:"delete"`
	PreservePermissions bool   `json:"preservePermissions"`
	PreserveOwner       bool   `json:"preserveOwner"`
	PreserveGroup       bool   `json:"preserveGroup"`
	PreserveTimes       bool   `json:"preserveTimes"`
	PreserveLinks       bool   `json:"preserveLinks"`
	PreserveACLs        bool   `json:"preserveAcls"`
	PreserveXattrs      bool   `json:"preserveXattrs"`
	Checksum            bool   `json:"checksum"`
	DryRun              bool   `json:"dryRun"`
	SkipSpaceCheck      bool   `json:"skipSpaceCheck"`
}

type Source struct {
	Path       string            `json:"path,omitempty"`
	WorkloadID string            `json:"workloadId,omitempty"`
	Metadata   map[string]string `json:"metadata,omitempty"`
}

type Schedule struct {
	Cron     string `json:"cron"`
	Timezone string `json:"timezone,omitempty"`
}

type Consistency struct {
	Mode string `json:"mode"`
}

type Retention struct {
	KeepWithinDays int `json:"keepWithinDays,omitempty"`
	Daily          int `json:"daily"`
	Weekly         int `json:"weekly"`
	Monthly        int `json:"monthly"`
}

type Repository struct {
	SchemaVersion int               `json:"schemaVersion"`
	ID            string            `json:"id"`
	Name          string            `json:"name"`
	Type          RepositoryType    `json:"type"`
	Location      string            `json:"location"`
	PasswordRef   string            `json:"passwordRef"`
	CredentialRef string            `json:"credentialRef,omitempty"`
	Mount         *MountConfig      `json:"mount,omitempty"`
	Options       map[string]string `json:"options,omitempty"`
}

type MountConfig struct {
	Managed    bool   `json:"managed"`
	Remote     string `json:"remote,omitempty"`
	MountPoint string `json:"mountPoint,omitempty"`
	ReadOnly   bool   `json:"readOnly,omitempty"`
}

type NotificationTarget struct {
	SchemaVersion int      `json:"schemaVersion"`
	ID            string   `json:"id"`
	Name          string   `json:"name"`
	Type          string   `json:"type"`
	Enabled       bool     `json:"enabled"`
	URL           string   `json:"url,omitempty"`
	Topic         string   `json:"topic,omitempty"`
	TokenRef      string   `json:"tokenRef,omitempty"`
	Events        []string `json:"events,omitempty"`
}

type Run struct {
	SchemaVersion     int        `json:"schemaVersion"`
	ID                string     `json:"id"`
	JobID             string     `json:"jobId,omitempty"`
	TaskType          string     `json:"taskType"`
	Status            string     `json:"status"`
	Priority          int        `json:"priority"`
	CreatedAt         time.Time  `json:"createdAt"`
	StartedAt         *time.Time `json:"startedAt,omitempty"`
	FinishedAt        *time.Time `json:"finishedAt,omitempty"`
	SnapshotID        string     `json:"snapshotId,omitempty"`
	Message           string     `json:"message,omitempty"`
	ErrorCode         string     `json:"errorCode,omitempty"`
	BytesAdded        int64      `json:"bytesAdded,omitempty"`
	FilesNew          int64      `json:"filesNew,omitempty"`
	FilesChanged      int64      `json:"filesChanged,omitempty"`
	BytesProcessed    int64      `json:"bytesProcessed,omitempty"`
	FilesProcessed    int64      `json:"filesProcessed,omitempty"`
	ProgressPercent   float64    `json:"progressPercent,omitempty"`
	ProgressBytes     int64      `json:"progressBytes,omitempty"`
	ProgressTotal     int64      `json:"progressTotal,omitempty"`
	ProgressFiles     int64      `json:"progressFiles,omitempty"`
	ProgressFileTotal int64      `json:"progressFileTotal,omitempty"`
	BytesPerSecond    float64    `json:"bytesPerSecond,omitempty"`
	SecondsRemaining  int64      `json:"secondsRemaining,omitempty"`
	CurrentFile       string     `json:"currentFile,omitempty"`
	LiveLog           []string   `json:"liveLog,omitempty"`
}

type RestoreTask struct {
	SchemaVersion int      `json:"schemaVersion"`
	ID            string   `json:"id"`
	RepositoryID  string   `json:"repositoryId"`
	SnapshotID    string   `json:"snapshotId"`
	Includes      []string `json:"includes"`
	Target        string   `json:"target"`
	InPlace       bool     `json:"inPlace"`
	Confirmed     bool     `json:"confirmed"`
}

type Snapshot struct {
	ID       string    `json:"id"`
	ShortID  string    `json:"short_id"`
	Time     time.Time `json:"time"`
	Hostname string    `json:"hostname"`
	Paths    []string  `json:"paths"`
	Tags     []string  `json:"tags"`
}

type RepositoryStats struct {
	RepositoryID   string `json:"repositoryId"`
	RepositoryName string `json:"repositoryName"`
	StoredBytes    int64  `json:"storedBytes"`
	FileCount      int64  `json:"fileCount"`
	SnapshotCount  int    `json:"snapshotCount"`
	Error          string `json:"error,omitempty"`
}

func DefaultConfig() Config {
	return Config{
		SchemaVersion: SchemaVersion,
		Jobs:          []Job{},
		Repositories:  []Repository{},
		Notifications: []NotificationTarget{{SchemaVersion: SchemaVersion, ID: "unraid", Name: "Unraid", Type: "unraid", Enabled: true, Events: []string{"success", "warning", "failed"}}},
		Settings: Settings{
			ResticPath:       "/usr/local/libexec/urbm/restic",
			RsyncPath:        "/usr/bin/rsync",
			RestoreRoot:      "/mnt/user/urbm-restores",
			CatchUpWindowHrs: 24,
			CheckCron:        "0 3 1 * *",
			PruneCron:        "0 4 * * 0",
		},
	}
}

func NormalizeConfig(c Config) Config {
	if strings.TrimSpace(c.Settings.RsyncPath) == "" {
		c.Settings.RsyncPath = "/usr/bin/rsync"
	}
	notifications := make([]NotificationTarget, 0, len(c.Notifications)+1)
	hasUnraid := false
	for _, target := range c.Notifications {
		if target.Type == "ntfy" {
			continue
		}
		if target.Type == "unraid" {
			hasUnraid = true
		}
		notifications = append(notifications, target)
	}
	if !hasUnraid {
		notifications = append(notifications, NotificationTarget{SchemaVersion: SchemaVersion, ID: "unraid", Name: "Unraid", Type: "unraid", Enabled: true, Events: []string{"success", "warning", "failed"}})
	}
	c.Notifications = notifications
	return c
}
