package main

import (
	"archive/tar"
	"bytes"
	"io"
	"io/fs"
	"log"
	"net/http"
	"os"
	"path/filepath"

	restful "github.com/emicklei/go-restful"
	"m.cluseau.fr/go/httperr"
	"novit.tech/direktil/local-server/secretstore"
)

type NamedPassphrase struct {
	Name       string
	Passphrase []byte
}

func wsUnlockStore(req *restful.Request, resp *restful.Response) {
	np := NamedPassphrase{}
	err := req.ReadEntity(&np)
	if err != nil {
		resp.WriteError(http.StatusBadRequest, err)
		return
	}

	defer secretstore.Memzero(np.Passphrase)

	if secStore.IsNew() {
		if len(np.Name) == 0 {
			wsBadRequest(resp, "no name given")
			return
		}
	}

	if len(np.Passphrase) == 0 {
		wsBadRequest(resp, "no passphrase given")
		return
	}

	if secStore.Unlocked() {
		if secStore.HasKey(np.Passphrase) {
			resp.WriteEntity(adminToken)
		} else {
			wsError(resp, ErrUnauthorized)
		}
		return
	}

	if err := unlockSecretStore(np.Name, np.Passphrase); err.Any() {
		err.WriteJSON(resp.ResponseWriter)
		return
	}

	resp.WriteEntity(adminToken)
}

func wsStoreDownload(req *restful.Request, resp *restful.Response) {
	token := req.QueryParameter("token")
	if token != wState.Get().Store.DownloadToken {
		wsError(resp, ErrInvalidToken)
		return
	}

	buf := new(bytes.Buffer)
	arch := tar.NewWriter(buf)

	root := os.DirFS(secStoreRoot())

	err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, readErr error) (err error) {
		if readErr != nil {
			err = readErr
			return
		}

		if path == "." {
			return
		}

		fi, err := d.Info()
		if err != nil {
			return
		}

		hdr, err := tar.FileInfoHeader(fi, "")
		if err != nil {
			return
		}

		hdr.Name = path
		hdr.Uid = 0
		hdr.Gid = 0

		err = arch.WriteHeader(hdr)
		if err != nil {
			return
		}

		if fi.IsDir() {
			return
		}

		f, err := root.Open(path)
		if err != nil {
			return
		}
		defer f.Close()

		io.Copy(arch, f)

		return
	})

	if err != nil {
		wsError(resp, err)
		return
	}

	err = arch.Close()
	if err != nil {
		wsError(resp, err)
		return
	}

	buf.WriteTo(resp)
}

func wsStoreUpload(req *restful.Request, resp *restful.Response) {
	if !secStore.IsNew() {
		wsError(resp, httperr.BadRequest("store is not new"))
		return
	}

	buf := new(bytes.Buffer)

	_, err := io.Copy(buf, req.Request.Body)
	if err != nil {
		wsError(resp, err)
		return
	}

	arch := tar.NewReader(buf)

	root := secStoreRoot()

	for {
		hdr, err := arch.Next()
		if err == io.EOF {
			err = nil
			break
		} else if err != nil {
			wsError(resp, err)
			return
		}

		log.Print(hdr.Name)

		fullPath := filepath.Join(root, hdr.Name)

		switch {
		case hdr.FileInfo().IsDir():
			err = os.MkdirAll(fullPath, 0700)
			if err != nil {
				wsError(resp, err)
				return
			}

		default:
			content, err := io.ReadAll(io.LimitReader(arch, hdr.Size))
			if err != nil {
				wsError(resp, err)
				return
			}

			err = os.WriteFile(fullPath, content, 0600)
			if err != nil {
				wsError(resp, err)
				return
			}
		}
	}

	if err != nil {
		wsError(resp, err)
		return
	}

	openSecretStore()

	resp.WriteEntity(map[string]any{"ok": true})
}