/*
  * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 *
 *
*/

package handler

import (
	"io"
	"net/http"

	"mstore/app/imageoper"
	"mstore/app/router"
	"mstore/pkg/terms"
)

// HEAD /v2/<name>/blobs/<digest> 200 404
func (hand *Handler) BlobExists(rctx *router.Context) {
	name, _ := rctx.GetSubpath("name")
	digest, _ := rctx.GetSubpath("digest")

	//hand.DumpHeaders("BlobExists", rctx)
	params := &imageoper.BlobExistsParams{
		Name:   name,
		Digest: digest,
	}
	// Rigth checking
	operatorID, _ := rctx.GetString(userTag)
	opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadImages, params.Name)
	if err != nil {
		rctx.SetStatus(http.StatusInternalServerError)
		return
	}
	if !opEnable {
		rctx.SetStatus(http.StatusMethodNotAllowed)
		return
	}
	// Execution of the operation
	ctx := rctx.GetContext()
	res, code, err := hand.imop.BlobExists(ctx, operatorID, params)
	if err != nil {
		hand.logg.Errorf("BlobExist error: %v", err)
	}
	if code == http.StatusOK {
		rctx.SetHeader("Docker-Content-Digest", res.DockerContentDigest)
		rctx.SetHeader("Content-Length", res.ContentLength)
		rctx.SetHeader("Content-Type", res.ContentType)

	}
	rctx.SetStatus(code)
}

// POST /v2/<name>/blobs/uploads/   202     404
// POST /v2/<name>/blobs/uploads/?digest=<digest>                  201/202 404/400
// POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<other_name> 201     404
func (hand *Handler) PostUpload(rctx *router.Context) {
	name, _ := rctx.GetSubpath("name")

	//hand.DumpHeaders("PostUploads", rctx)
	authorization := rctx.GetHeader("Authorization")
	if authorization == "" {
		rctx.SetHeader("WWW-Authenticate", `Basic realm="mstore"`)
		rctx.SetStatus(http.StatusUnauthorized)
		return
	}

	digest := rctx.GetQuery("digest")
	mount := rctx.GetQuery("mount")
	from := rctx.GetQuery("from")

	params := &imageoper.PostUploadParams{
		Name:   name,
		Digest: digest,
		Mount:  mount,
		From:   from,
	}
	// Rigth checking
	operatorID, _ := rctx.GetString(userTag)
	opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
	if err != nil {
		rctx.SetStatus(http.StatusInternalServerError)
		return
	}
	if !opEnable {
		rctx.SetStatus(http.StatusMethodNotAllowed)
		return
	}
	// Execution of the operation
	res, code, err := hand.imop.PostUpload(rctx.Ctx, operatorID, params)
	if err != nil {
		hand.logg.Errorf("PostUpload error: %v", err)
	} else {
		rctx.SetHeader("Location", res.Location)
		rctx.SetHeader("Content-Length", res.ContentLength)
		rctx.SetHeader("Docker-Upload-UUID", string(res.DockerUploadUUID))
	}
	rctx.SetStatus(code)
}

// PATCH /v2/<name>/blobs/uploads/<reference>                202   404/416
func (hand *Handler) PatchUpload(rctx *router.Context) {

	contentLength := rctx.GetHeader("Content-Length")
	contentType := rctx.GetHeader("Content-Type")
	contentRange := rctx.GetHeader("Content-Range")

	name, _ := rctx.GetSubpath("name")
	reference, _ := rctx.GetSubpath("reference")
	reader := rctx.Request.Body

	params := &imageoper.PatchUploadParams{
		ContentLength: contentLength,
		ContentType:   contentType,
		ContentRange:  contentRange,
		Name:          name,
		Reference:     reference,
		Reader:        reader,
	}
	// Rigth checking
	operatorID, _ := rctx.GetString(userTag)
	opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
	if err != nil {
		rctx.SetStatus(http.StatusInternalServerError)
		return
	}
	if !opEnable {
		rctx.SetStatus(http.StatusMethodNotAllowed)
		return
	}
	// Execution of the operation
	ctx := rctx.GetContext()
	res, code, err := hand.imop.PatchUpload(ctx, operatorID, params)
	if err != nil {
		hand.logg.Errorf("PatchUpload error: %v", err)
	}
	rctx.SetHeader("Location", res.Location)
	rctx.SetHeader("Range", res.Range)

	rctx.SetStatus(code)
}

// PUT /v2/<name>/blobs/uploads/<reference>?digest=<digest>  202   404/416
// PUT /v2/<name>/uploads/<reference>?digest=<digest>        202   404/416
func (hand *Handler) PutUpload(rctx *router.Context) {

	contentType := rctx.GetHeader("Content-Type")
	contentLength := rctx.GetHeader("Content-Length")
	contentRange := rctx.GetHeader("Content-Range")

	name, _ := rctx.GetSubpath("name")
	reference, _ := rctx.GetSubpath("reference")

	digest := rctx.GetQuery("digest")
	reader := rctx.Request.Body

	params := &imageoper.PutUploadParams{
		ContentLength: contentLength,
		ContentType:   contentType,
		ContentRange:  contentRange,
		Name:          name,
		Reference:     reference,
		Digest:        digest,
		Reader:        reader,
	}
	// Rigth checking
	operatorID, _ := rctx.GetString(userTag)
	opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
	if err != nil {
		rctx.SetStatus(http.StatusInternalServerError)
		return
	}
	if !opEnable {
		rctx.SetStatus(http.StatusMethodNotAllowed)
		return
	}
	// Execution of the operation
	res, code, err := hand.imop.PutUpload(rctx.Ctx, operatorID, params)
	if err != nil {
		hand.logg.Errorf("PutUpload error: %v", err)
	}
	rctx.SetHeader("Location", res.Location)
	rctx.SetStatus(code)
}

// GET /v2/<name>/blobs/<digest> 200 404
func (hand *Handler) GetBlob(rctx *router.Context) {

	name, _ := rctx.GetSubpath("name")
	digest, _ := rctx.GetSubpath("digest")

	params := &imageoper.GetBlobParams{
		Name:   name,
		Digest: digest,
	}
	// Rigth checking
	operatorID, _ := rctx.GetString(userTag)
	opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadImages, params.Name)
	if err != nil {
		rctx.SetStatus(http.StatusInternalServerError)
		return
	}
	if !opEnable {
		rctx.SetStatus(http.StatusMethodNotAllowed)
		return
	}
	// Execution of the operation
	ctx := rctx.GetContext()
	res, code, err := hand.imop.GetBlob(ctx, operatorID, params)
	if err != nil {
		hand.logg.Errorf("GetBlob error: %v", err)
	}

	if code == http.StatusOK {
		rctx.SetHeader("Content-Length", res.ContentLength)
		rctx.SetHeader("Content-Type", res.ContentType)
		rctx.SetHeader("Docker-Content-Digest", res.DockerContentDigest)
		rctx.SetStatus(code)

		defer res.ReadCloser.Close()
		_, err = io.Copy(rctx.Writer, res.ReadCloser)
		if err != nil {
			hand.logg.Errorf("GetBlob error: %v", err)
			rctx.SetStatus(http.StatusInternalServerError)
			return
		}
		return
	}
	rctx.SetStatus(code)
}

// DELETE /v2/<name>/blobs/<digest> 202 404/405
func (hand *Handler) DeleteBlob(rctx *router.Context) {

	name, _ := rctx.GetSubpath("name")
	digest, _ := rctx.GetSubpath("digest")

	params := &imageoper.DeleteBlobParams{
		Name:   name,
		Digest: digest,
	}
	// Rigth checking
	operatorID, _ := rctx.GetString(userTag)
	opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
	if err != nil {
		rctx.SetStatus(http.StatusInternalServerError)
		return
	}
	if !opEnable {
		rctx.SetStatus(http.StatusMethodNotAllowed)
		return
	}
	// Execution of the operation
	ctx := rctx.GetContext()
	_, code, err := hand.imop.DeleteBlob(ctx, operatorID, params)
	if err != nil {
		hand.logg.Errorf("DeleteBlob error: %v", err)
	}

	rctx.SetStatus(code)
}
