package sync

import (
	"errors"
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"

	"github.com/studio-b12/gowebdav"
)

// CloudImporter implements an Importer, where files to imports are located
// remotely, under a WebDAV server (such as sabre/dav, owncloud, ...).
type CloudImporter struct {
	// baseDAV is the URL (most probably http or https one) to the root directory.
	// It should contains all themes, in separated directories.
	baseDAV url.URL
	// username is the username used to perform authentication through BasicAuth.
	username string
	// password is the password used to perform authentication through BasicAuth.
	password string
}

// NewCloudImporter registers a new object CloudImporter, as the URL conversion
// can returns errors.
func NewCloudImporter(baseDAV string, username string, password string) (*CloudImporter, error) {
	if r, err := url.Parse(baseDAV); err != nil {
		return nil, err
	} else {
		return &CloudImporter{*r, username, password}, nil
	}
}

func (i CloudImporter) Kind() string {
	return "cloud file importer: " + i.baseDAV.String()
}

func (i CloudImporter) Id() *string {
	return nil
}

func (i CloudImporter) Init() error {
	return nil
}

func (i CloudImporter) Sync() error {
	return nil
}

func (i CloudImporter) Exists(filename string) bool {
	fullURL := i.baseDAV
	fullURL.Path = path.Join(fullURL.Path, filename)

	client := http.Client{}
	if req, err := http.NewRequest("HEAD", fullURL.String(), nil); err == nil {
		req.SetBasicAuth(i.username, i.password)

		if resp, err := client.Do(req); err == nil {
			defer resp.Body.Close()

			return resp.StatusCode == http.StatusOK
		}
	}
	return false
}

func (i CloudImporter) toURL(filename string) string {
	fullURL := i.baseDAV
	fullURL.Path = path.Join(fullURL.Path, filename)
	return fullURL.String()
}

func (i CloudImporter) importFile(URI string, next func(string, string) (interface{}, error)) (interface{}, error) {
	return ImportFile(i, URI, next)
}

func (i CloudImporter) GetFile(filename string) (io.Reader, error) {
	fullURL := i.baseDAV
	fullURL.Path = path.Join(fullURL.Path, filename)

	client := http.Client{}
	if req, err := http.NewRequest("GET", fullURL.String(), nil); err != nil {
		return nil, err
	} else {
		req.SetBasicAuth(i.username, i.password)
		if resp, err := client.Do(req); err != nil {
			return nil, err
		} else {
			if resp.StatusCode != http.StatusOK {
				resp.Body.Close()
				return nil, errors.New(resp.Status)
			} else {
				return resp.Body, nil
			}
		}
	}
}

func (i CloudImporter) writeFile(filename string, reader io.Reader) error {
	fullURL := i.baseDAV
	fullURL.Path = path.Join(fullURL.Path, filename)

	client := http.Client{}
	if req, err := http.NewRequest("PUT", fullURL.String(), reader); err != nil {
		return err
	} else {
		req.SetBasicAuth(i.username, i.password)
		if resp, err := client.Do(req); err != nil {
			return err
		} else {
			defer resp.Body.Close()

			if resp.StatusCode != http.StatusOK {
				return errors.New(resp.Status)
			} else {
				return nil
			}
		}
	}
}

func (i CloudImporter) ListDir(filename string) ([]string, error) {
	client := gowebdav.NewClient(i.baseDAV.String(), i.username, i.password)

	if files, err := client.ReadDir(strings.Replace(url.PathEscape(filename), "%2F", "/", -1)); err != nil {
		return nil, err
	} else {
		res := make([]string, 0)
		for _, file := range files {
			res = append(res, file.Name())
		}
		return res, nil
	}
}

func (i CloudImporter) Stat(filename string) (os.FileInfo, error) {
	return gowebdav.NewClient(i.baseDAV.String(), i.username, i.password).Stat(strings.Replace(url.PathEscape(filename), "%2F", "/", -1))
}
