package wiki

import (
	"net/http"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/dalu/wiki/storage"
	"github.com/dalu/wiki/template"
	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"
	"gopkg.in/mgo.v2/bson"
)

const DefaultMountPath = "/wiki/"

const (
	methodGET = "GET"
	methodPOST = "POST"
)

type wikiHandler struct {
	s storage.Interface
	r template.Interface
	l *logrus.Logger
}

func NewWikiHandler(s storage.Interface, r template.Interface, l *logrus.Logger) *wikiHandler {
	l.Level = logrus.DebugLevel
	return &wikiHandler{
		s: s,
		r: r,
		l: l,
	}
}

func (h *wikiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case methodGET:
		h.handleGET(w, r)
	case methodPOST:
		h.handlePOST(w, r)
	default:
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
	}
}

func (h *wikiHandler) handleGET(w http.ResponseWriter, r *http.Request) {
	action := r.URL.Query().Get("action")

	switch action {
	case "":
		h.show(w, r)
	case "edit":
		h.edit(w, r)
	case "create":
		h.create(w, r)
	case "revision":
		h.l.Debug("revision hit")
	case "revisions":
		h.l.Debug("revisions hit")
	}
}

func (h *wikiHandler) handlePOST(w http.ResponseWriter, r *http.Request) {
	switch r.FormValue("action") {
	case "create":
		h.createPOST(w, r)
	case "edit":
		h.editPOST(w, r)
	default:
		return
	}
}

// ----- POST handlers -----

func (h *wikiHandler) editPOST(w http.ResponseWriter, r *http.Request) {
	termID, text := "", ""
	if strings.Contains(r.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {
		termID = r.FormValue("termID")
		text = r.FormValue("text")
	}
	t, e := h.s.GetTermByID(termID)
	if e != nil {
		h.l.Error("handlePOST:", e)
		return
	}
	if t.Slug != r.URL.Path {
		h.l.Errorf("handlePOST:path mismatch: %s %s", t.Slug, r.URL.Path)
		return
	}
	revision := new(storage.Revision)
	revision.ID = bson.NewObjectId()
	revision.TermID = termID
	revision.Text = text
	revision.PublishDate = time.Now()
	revision.AuthorIP = r.RemoteAddr

	if e := h.s.CreateRevision(revision); e != nil {
		h.l.Error("handlePOST.CreateRevision:", e)
		return
	}
	t.CurrentRevisionID = revision.ID.Hex()
	if e := h.s.UpdateTerm(t); e != nil {
		h.l.Error("handlePOST.UpdateTerm:", e)
		return
	}
	w.WriteHeader(200)

}

func (h *wikiHandler) createPOST(w http.ResponseWriter, r *http.Request) {

}


// ----- GET handlers -----

func (h *wikiHandler) show(w http.ResponseWriter, r *http.Request) {
	h.l.Debug("show hit")
	term, e := h.s.GetTermBySlug(r.URL.Path)
	if e != nil {
		h.notFound(w, r)
		return
	}
	if term.Redirect != "" {
		http.Redirect(w, r, DefaultMountPath + term.Redirect, http.StatusTemporaryRedirect)
		return
	}
	if term.CurrentRevisionID == "" {
		h.notFound(w, r)
		//http.Redirect(w, r, DefaultMountPath+r.URL.Path+"?action=new", http.StatusTemporaryRedirect)
		return
	}
	revision, e := h.s.GetRevisionByID(term.CurrentRevisionID)
	if e != nil {
		h.l.Error("wikihandler.show.GetRevisionByTermID:", e)
		return
	}

	revision.HTML = string(bluemonday.UGCPolicy().SanitizeBytes(blackfriday.MarkdownCommon([]byte(revision.Text))))

	ctx := make(map[string]interface{})
	ctx["term"] = term
	ctx["revision"] = revision

	if e := h.r.Render(w, "wiki/show", ctx); e != nil {
		h.l.Error("wikiHandler.show.Renderer.write:", e)
	}
}

func (h *wikiHandler) notFound(w http.ResponseWriter, r *http.Request) {
	h.l.Debug("not found hit")
	w.WriteHeader(http.StatusNotFound)

	ctx := make(map[string]interface{})
	ctx["term"] = unslugify(r.URL.Path)
	ctx["path"] = r.URL.Path
	if e := h.r.Render(w, "wiki/not-found", ctx); e != nil {
		h.l.Error("wikiHandler.notFound.Renderer.write:", e)
	}
}

func (h *wikiHandler) create(w http.ResponseWriter, r *http.Request) {
	h.l.Debug("create hit")
	ctx := make(map[string]interface{})
	ctx["term"] = unslugify(r.URL.Path)
	if e := h.r.Render(w, "wiki/create", ctx); e != nil {
		h.l.Error("wikiHandler.create.Renderer.write:", e)
	}
}

func (h *wikiHandler) edit(w http.ResponseWriter, r *http.Request) {
	h.l.Debug("edit hit")
	term, e := h.s.GetTermBySlug(r.URL.Path)
	if e != nil {
		http.NotFound(w, r)
		return
	}
	revision, e := h.s.GetRevisionByID(term.CurrentRevisionID)
	if e != nil {
		h.l.Error("wikihandler.show.GetRevisionByTermID:", e)
		return
	}

	ctx := make(map[string]interface{})
	ctx["term"] = term
	ctx["revision"] = revision

	if e := h.r.Render(w, "wiki/edit", ctx); e != nil {
		h.l.Error("wikiHandler.edit.Renderer.write:", e)
	}

}
