package proxmox

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"syscall"

	"github.com/rancher/os/config/cloudinit/datasource"
	"github.com/rancher/os/pkg/log"
	"github.com/rancher/os/pkg/util"

	"github.com/docker/docker/pkg/mount"
)

const (
	configDev           = "/dev/sr0"
	configDevMountPoint = "/media/pve-config"
)

type Proxmox struct {
	root                string
	readFile            func(filename string) ([]byte, error)
	lastError           error
	availabilityChanges bool
}

func NewDataSource(root string) *Proxmox {
	return &Proxmox{root, ioutil.ReadFile, nil, true}
}

func (pve *Proxmox) IsAvailable() bool {
	if pve.root == configDevMountPoint {
		pve.lastError = MountConfigDrive()
		if pve.lastError != nil {
			log.Error(pve.lastError)
			pve.availabilityChanges = false
			return false
		}
		defer pve.Finish()
	}

	_, pve.lastError = os.Stat(pve.root)
	return !os.IsNotExist(pve.lastError)
}

func (pve *Proxmox) Finish() error {
	return UnmountConfigDrive()
}

func (pve *Proxmox) String() string {
	if pve.lastError != nil {
		return fmt.Sprintf("%s: %s (lastError: %v)", pve.Type(), pve.root, pve.lastError)
	}
	return fmt.Sprintf("%s: %s", pve.Type(), pve.root)
}

func (pve *Proxmox) AvailabilityChanges() bool {
	return pve.availabilityChanges
}

func (pve *Proxmox) ConfigRoot() string {
	return pve.root
}

func (pve *Proxmox) FetchMetadata() (metadata datasource.Metadata, err error) {
	return datasource.Metadata{}, nil
}

func (pve *Proxmox) FetchUserdata() ([]byte, error) {
	return pve.tryReadFile(path.Join(pve.root, "user-data"))
}

func (pve *Proxmox) Type() string {
	return "proxmox"
}

func (pve *Proxmox) tryReadFile(filename string) ([]byte, error) {
	if pve.root == configDevMountPoint {
		pve.lastError = MountConfigDrive()
		if pve.lastError != nil {
			log.Error(pve.lastError)
			return nil, pve.lastError
		}
		defer pve.Finish()
	}
	log.Debugf("Attempting to read from %q\n", filename)
	data, err := pve.readFile(filename)
	if os.IsNotExist(err) {
		err = nil
	}
	if err != nil {
		log.Errorf("ERROR read cloud-config file(%s) - err: %q", filename, err)
	}
	return data, err
}

func MountConfigDrive() error {
	if err := os.MkdirAll(configDevMountPoint, 700); err != nil {
		return err
	}

	fsType, err := util.GetFsType(configDev)
	if err != nil {
		return err
	}

	return mount.Mount(configDev, configDevMountPoint, fsType, "ro")
}

func UnmountConfigDrive() error {
	return syscall.Unmount(configDevMountPoint, 0)
}
