package photosapi

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

func (s *Service) UploadCreate(c *gin.Context) {
	sha, err := uuid.NewRandom()
	if err != nil {
		s.Error(c, http.StatusInternalServerError, err)
		return
	}

	if err := s.Storage.Create(StorageTmp, sha.String()); err != nil {
		s.Error(c, http.StatusInternalServerError, err)
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"status":    "success",
		"upload_id": sha.String(),
	})
}

type UploadPartRequest struct {
	UploadId string `uri:"upload_id" binding:"required,uuid"`
	Part     uint   `uri:"part" binding:"required"`
}

func (s *Service) UploadPart(c *gin.Context) {
	var uploadPart UploadPartRequest
	if err := c.ShouldBindUri(&uploadPart); err != nil {
		s.Error(c, http.StatusBadRequest, err)
		return
	}

	if !s.Storage.Exists(StorageTmp, uploadPart.UploadId) {
		s.Error(c, http.StatusNotFound, errors.New("upload id doesn't exists"))
		return
	}

	f, err := os.Create(
		s.Storage.Join(StorageTmp, uploadPart.UploadId, fmt.Sprint(uploadPart.Part)),
	)
	if err != nil {
		s.Error(c, http.StatusInternalServerError, err)
		return
	}

	defer f.Close()
	defer c.Request.Body.Close()

	sha := sha256.New()
	t := io.TeeReader(c.Request.Body, sha)
	w, err := io.Copy(f, t)
	if err != nil {
		s.Error(c, http.StatusInternalServerError, err)
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"upload_id": uploadPart.UploadId,
		"part":      uploadPart.Part,
		"size":      w,
		"sha256":    hex.EncodeToString(sha.Sum(nil)),
		"status":    "success",
	})
}

func (s *Service) UploadCancel(c *gin.Context) {
	upload_id := c.Param("upload_id")

	if err := s.Storage.Delete(StorageTmp, upload_id); err != nil {
		s.Error(c, http.StatusNotFound, errors.New("upload id doesn't exists"))
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"status": "success",
	})
}

type UploadCompleteRequest struct {
	SHA256 string `json:"sha256" binding:"required,lowercase,alphanum,len=64"`
	Name   string `json:"name" binding:"required"`
	Parts  uint   `json:"parts" binding:"required"`
}

func (s *Service) UploadComplete(c *gin.Context) {
	var uploadCompleteRequest UploadCompleteRequest
	if err := c.ShouldBindJSON(&uploadCompleteRequest); err != nil {
		s.Error(c, http.StatusBadRequest, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"sha256": uploadCompleteRequest.SHA256,
		"parts":  uploadCompleteRequest.Parts,
		"name":   uploadCompleteRequest.Name,
	})
}

func (s *Service) UploadInit() {
	upload := s.Gin.Group("/upload")
	upload.GET("/create", s.UploadCreate)
	upload.POST("/part/:upload_id/:part", s.UploadPart)
	upload.GET("/cancel/:upload_id", s.UploadCancel)
	upload.POST("/complete/:upload_id", s.UploadComplete)
}