package ctrl

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	. "github.com/mickael-kerjean/filestash/server/common"
	"github.com/mickael-kerjean/filestash/server/model"
	"net/http"
	"strings"
)

func ShareList(ctx *App, res http.ResponseWriter, req *http.Request) {
	path, err := PathBuilder(ctx, req.URL.Query().Get("path"))
	if err != nil {
		SendErrorResult(res, err)
		return
	}
	listOfSharedLinks, err := model.ShareList(
		GenerateID(ctx.Session),
		path,
	)
	if err != nil {
		Log.Debug("share::list '%s'", err.Error())
		SendErrorResult(res, err)
		return
	}

	for i := 0; i < len(listOfSharedLinks); i++ {
		listOfSharedLinks[i].Path = "/" + strings.TrimPrefix(listOfSharedLinks[i].Path, path)
	}
	SendSuccessResults(res, listOfSharedLinks)
}

func ShareUpsert(ctx *App, res http.ResponseWriter, req *http.Request) {
	share_id := mux.Vars(req)["share"]
	if share_id == "private" {
		Log.Debug("share::upsert 'private'")
		SendErrorResult(res, ErrNotValid)
		return
	}
	s := Share{
		Id: share_id,
		Auth: func() string {
			if ctx.Share.Id == "" {
				str := ""
				index := 0
				for {
					cookie, err := req.Cookie(CookieName(index))
					if err != nil {
						break
					}
					index++
					str += cookie.Value
				}
				return str
			}
			return ctx.Share.Auth
		}(),
		Backend: func() string {
			if ctx.Share.Id == "" {
				return GenerateID(ctx.Session)
			}
			return ctx.Share.Backend
		}(),
		Path: func() string {
			leftPath := "/"
			rightPath := strings.TrimPrefix(NewStringFromInterface(ctx.Body["path"]), "/")
			if ctx.Share.Id != "" {
				leftPath = ctx.Share.Path
			} else {
				if ctx.Session["path"] != "" {
					leftPath = ctx.Session["path"]
				}
			}
			return leftPath + rightPath
		}(),
		Password:     NewStringpFromInterface(ctx.Body["password"]),
		Users:        NewStringpFromInterface(ctx.Body["users"]),
		Expire:       NewInt64pFromInterface(ctx.Body["expire"]),
		Url:          NewStringpFromInterface(ctx.Body["url"]),
		CanManageOwn: NewBoolFromInterface(ctx.Body["can_manage_own"]),
		CanShare:     NewBoolFromInterface(ctx.Body["can_share"]),
		CanRead:      NewBoolFromInterface(ctx.Body["can_read"]),
		CanWrite:     NewBoolFromInterface(ctx.Body["can_write"]),
		CanUpload:    NewBoolFromInterface(ctx.Body["can_upload"]),
	}
	if err := model.ShareUpsert(&s); err != nil {
		Log.Debug("share::upsert '%s'", err.Error())
		SendErrorResult(res, err)
		return
	}
	SendSuccessResult(res, nil)
}

func ShareDelete(ctx *App, res http.ResponseWriter, req *http.Request) {
	share_target := mux.Vars(req)["share"]
	if err := model.ShareDelete(share_target); err != nil {
		Log.Debug("share::delete '%s'", err.Error())
		SendErrorResult(res, err)
		return
	}
	SendSuccessResult(res, nil)
}

func ShareVerifyProof(ctx *App, res http.ResponseWriter, req *http.Request) {
	var submittedProof model.Proof
	var verifiedProof []model.Proof
	var requiredProof []model.Proof
	var remainingProof []model.Proof
	var s Share
	var err error

	// 1) initialise the current context
	share_id := mux.Vars(req)["share"]
	s, err = model.ShareGet(share_id)
	if err != nil {
		Log.Debug("share::verify::init '%s'", err.Error())
		SendErrorResult(res, err)
		return
	}
	submittedProof = model.Proof{
		Key:   fmt.Sprint(ctx.Body["type"]),
		Value: fmt.Sprint(ctx.Body["value"]),
	}
	verifiedProof = model.ShareProofGetAlreadyVerified(req)
	requiredProof = model.ShareProofGetRequired(s)

	// 2) validate the current context
	if len(verifiedProof) > 20 || len(requiredProof) > 20 {
		http.SetCookie(res, &http.Cookie{
			Name:   COOKIE_NAME_PROOF,
			Value:  "",
			MaxAge: -1,
			Path:   COOKIE_PATH,
		})
		Log.Debug("share::verify::validate 'proof issue' len(verifiedProof)[%d] len(requiredProof)[%d]", len(verifiedProof), len(requiredProof))
		SendErrorResult(res, ErrNotValid)
		return
	}
	if err := s.IsValid(); err != nil {
		Log.Debug("share::verify::validate '%s'", err.Error())
		SendErrorResult(res, err)
		return
	}

	// 3) process the proof sent by the user
	submittedProof, err = model.ShareProofVerifier(s, submittedProof)
	if err != nil {
		Log.Debug("share::verify::process '%s'", err.Error())
		submittedProof.Error = NewString(err.Error())
		SendSuccessResult(res, submittedProof)
		return
	}
	if submittedProof.Key == "code" {
		submittedProof.Value = ""
		submittedProof.Message = NewString("We've sent you a message with a verification code")
		SendSuccessResult(res, submittedProof)
		return
	}

	if submittedProof.Key != "" {
		submittedProof.Id = Hash(submittedProof.Key+"::"+submittedProof.Value, 20)
		alreadyExist := false
		for i := 0; i < len(verifiedProof); i++ {
			if verifiedProof[i].Id == submittedProof.Id {
				alreadyExist = true
				break
			}
		}
		if alreadyExist == false {
			verifiedProof = append(verifiedProof, submittedProof)
		}
	}

	// 4) Find remaining proofs: requiredProof - verifiedProof
	remainingProof = model.ShareProofCalculateRemainings(requiredProof, verifiedProof)

	// 5) persist proofs in client cookie
	cookie := http.Cookie{
		Name: COOKIE_NAME_PROOF,
		Value: func(p []model.Proof) string {
			j, _ := json.Marshal(p)
			str, _ := EncryptString(SECRET_KEY_DERIVATE_FOR_PROOF, string(j))
			return str
		}(verifiedProof),
		Path:     COOKIE_PATH,
		MaxAge:   60 * 60 * 24 * 30,
		HttpOnly: true,
		SameSite: http.SameSiteNoneMode,
		Secure:   true,
	}
	http.SetCookie(res, &cookie)

	if len(remainingProof) > 0 {
		SendSuccessResult(res, remainingProof[0])
		return
	}

	SendSuccessResult(res, struct {
		Id        string `json:"id"`
		Path      string `json:"path"`
		CanRead   bool   `json:"can_read"`
		CanWrite  bool   `json:"can_write"`
		CanUpload bool   `json:"can_upload"`
	}{
		Id:        s.Id,
		Path:      s.Path,
		CanRead:   s.CanRead,
		CanWrite:  s.CanWrite,
		CanUpload: s.CanUpload,
	})
}
