package caddyhugo

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"sync"

	"github.com/spf13/afero"

	"git.stephensearles.com/stephen/acedoc"
	"git.stephensearles.com/stephen/idleshut"
)

type editSession struct {
	clients  uint
	docname  string
	filename string
	doc      *acedoc.Document
	tmpfs    afero.Fs
	mtx      sync.Mutex
}

func (es *editSession) Clients() uint {
	es.mtx.Lock()
	defer es.mtx.Unlock()

	return es.clients
}

func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) {
	filename := ch.docFilename(docName)

	fmt.Println("opening", filename)
	contents, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	tmpfs := afero.NewCopyOnWriteFs(afero.NewOsFs(), afero.NewMemMapFs())

	es := &editSession{
		docname:  docName,
		filename: filename,
		doc:      acedoc.NewString(string(contents)),
		tmpfs:    tmpfs,
	}

	err = es.doc.LogToFile(path.Join(ch.Dir, "logs", docName))
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	ch.docs[filename] = es

	err = ch.renderDraft(es)
	if err != nil {
		return nil, err
	}

	return es, nil
}

func (ch *CaddyHugo) renderDraft(es *editSession) error {
	var proc *idleshut.Process

	f := func() {
		if es.clients != 0 {
			proc.Touch()
		}
	}

	cfg, err := HugoInternalProcessConfig(ch, es, f)
	if err != nil {
		return fmt.Errorf("rendering draft: %v", err)
	}

	proc = idleshut.New(cfg)
	return proc.Start()
}

func (ch *CaddyHugo) persistAllEdits() error {
	ch.mtx.Lock()
	defer ch.mtx.Unlock()
	for _, es := range ch.docs {
		err := ch.persistEditsForSession(es)
		if err != nil {
			return err
		}
	}
	return nil
}

func (ch *CaddyHugo) persistEditsForSession(es *editSession) error {
	err := afero.WriteFile(afero.NewOsFs(), es.filename, []byte(es.doc.Contents()), 0644)
	if err != nil {
		return err
	}
	return nil
}

func (ch *CaddyHugo) hasEditSession(docName string) (*editSession, bool) {
	dr, ok := ch.docs[ch.docFilename(docName)]
	return dr, ok
}

func (ch *CaddyHugo) editSession(docName string) (*editSession, error) {
	ch.mtx.Lock()
	defer ch.mtx.Unlock()

	var err error

	dr, ok := ch.hasEditSession(docName)
	if !ok {
		dr, err = ch.newEditSession(docName)
	}

	return dr, err
}

func docNameFromEditRequest(r *http.Request) string {
	return r.URL.Path[len("/hugo/edit/"):]
}

func docNameFromCommentRequest(r *http.Request) string {
	return r.URL.Path[1 : len(r.URL.Path)-len("/comments")]
}
