package appexport

import (
	"encoding/json"
	"time"
)

// ManifestVersion is the current bundle format version.
const ManifestVersion = 1

// Manifest is the JSON metadata stored inside a .fab file.
type Manifest struct {
	Version        int       `json:"version"`
	AppName        string    `json:"app_name"`
	DisplayName    string    `json:"display_name"`
	ExportedAt     time.Time `json:"exported_at"`
	ControllerVer  string    `json:"controller_version"`
	NeedsHDD       bool      `json:"needs_hdd"`
	Encrypted      bool      `json:"encrypted"`
	HasDatabase    bool      `json:"has_database"`
	HasHDDData     bool      `json:"has_hdd_data"`
	HasVolumeData  bool      `json:"has_volume_data"`
	DBType         string    `json:"db_type,omitempty"`
	TotalSizeBytes int64     `json:"total_size_bytes"`
	ConfigFiles    []string  `json:"config_files"`
	VolumeNames    []string  `json:"volume_names,omitempty"`
	HDDSubdirs     []string  `json:"hdd_subdirs,omitempty"`
}

// Marshal returns the manifest as indented JSON.
func (m *Manifest) Marshal() ([]byte, error) {
	return json.MarshalIndent(m, "", "  ")
}

// UnmarshalManifest parses a manifest from JSON bytes.
func UnmarshalManifest(data []byte) (*Manifest, error) {
	var m Manifest
	if err := json.Unmarshal(data, &m); err != nil {
		return nil, err
	}
	// [CTRL-001] Reject path-traversal in any segment used to build a filesystem
	// path on import (app_name, hdd_subdirs, volume_names). A hostile .fab must
	// fail to parse rather than escape the stacks / HDD destination dir.
	if err := validateManifestPaths(&m); err != nil {
		return nil, err
	}
	return &m, nil
}
