package notebook

import (
	"crypto/tls"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"strings"

	"github.com/go-resty/resty/v2"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/cloudbrain"
	"code.gitea.io/gitea/modules/setting"

	"code.gitea.io/gitea/modules/log"
)

var restyClient *resty.Client

type NotebookApiResponse struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

type NotebookContent struct {
	Url      string
	Path     string
	Cookies  []*http.Cookie
	Xsrf     string
	PathType string //file  directory
	Token    string
}

func (c *NotebookContent) IsNotebookFileCanBrowser() bool {
	if c.Xsrf == "" {
		c.SetCookiesAndCsrf()
	}
	if c.Xsrf == "" {
		log.Warn("xsrf is empty, can not broswer url:" + c.Url)
		return false
	}
	return c.IsNoteBookContentsExist()

}

func (c *NotebookContent) SetCookiesAndCsrf() {
	log.Info("jupyter url:" + c.Url)
	var cookies []*http.Cookie
	const retryTimes = 10
	url := c.Url
	if c.Token != "" {
		url = c.Url + "?token=" + c.Token
	}
	for i := 0; i < retryTimes; i++ {
		res, err := http.Get(url)
		if err != nil {
			log.Error("browser jupyterUrl failed.", err)
			if i == retryTimes-1 {
				c.Cookies = cookies
			}

		} else {
			cookies = res.Cookies()
			xsrf := ""
			for _, cookie := range cookies {
				if cookie.Name == "_xsrf" {
					xsrf = cookie.Value
					if len(cookies) > 1 {
						break
					}

				}

			}
			if xsrf != "" {
				c.Cookies = cookies
				c.Xsrf = xsrf
			}

		}
	}
	c.Cookies = cookies

}

func (c *NotebookContent) IsNoteBookContentsExist() bool {
	client := getRestyClient()
	uploadUrl := getJupyterBaseUrl(c.Url) + "api/contents/" + c.Path + "?type=" + c.PathType
	res, err := client.R().
		SetCookies(c.Cookies).
		SetHeader("X-XSRFToken", c.Xsrf).
		Get(uploadUrl)
	if err != nil {
		log.Warn("browser url error:"+uploadUrl, err)
		return false
	}
	return res.StatusCode() == http.StatusOK
}

func (c *NotebookContent) UploadNoteBookFile(task *models.Cloudbrain) error {

	err := c.MakeNoteBookDir()
	if err != nil {
		return err
	}

	codePath := setting.JobPath + task.JobName + cloudbrain.CodeMountPath
	fileContents, err := ioutil.ReadFile(codePath + "/" + c.Path)
	if err != nil {
		log.Error("read jupyter file failed:%v", task.DisplayJobName, err)
	}

	base64Content := base64.StdEncoding.EncodeToString(fileContents)
	client := getRestyClient()
	uploadUrl := getJupyterBaseUrl(c.Url) + "api/contents/" + c.Path
	res, err := client.R().
		SetCookies(c.Cookies).
		SetHeader("X-XSRFToken", c.Xsrf).
		SetBody(map[string]interface{}{
			"type":    "file",
			"format":  "base64",
			"name":    path.Base(c.Path),
			"path":    c.Path,
			"content": base64Content}).
		Put(uploadUrl)
	if err != nil {
		log.Error("upload jupyter file failed:%v", task.DisplayJobName, err)
		return err
	} else if res.StatusCode() != http.StatusCreated {
		log.Error("upload jupyter file failed:%v, status is %s", task.DisplayJobName, res.Status(), err)
		return fmt.Errorf("status:", res.StatusCode())
	}
	return nil
}

/**
  if c.Path is   a/b/c.txt
  makedir  a/b
  if c.Path is   a/b/c
  makedir  a/b
*/
func (c *NotebookContent) MakeNoteBookDir() error {
	filePaths := strings.Split(c.Path, "/")

	for i := 0; i < len(filePaths)-1; i++ {
		cTemp := &NotebookContent{
			Url:      c.Url,
			Cookies:  c.Cookies,
			Path:     path.Join(filePaths[0 : i+1]...),
			PathType: "directory",
			Xsrf:     c.Xsrf,
		}
		if !cTemp.IsNoteBookContentsExist() {

			createTempDirUrl := getJupyterBaseUrl(cTemp.Url) + "api/contents/" + cTemp.Path
			client := getRestyClient()
			var jobResult NotebookApiResponse
			res, err := client.R().
				SetCookies(c.Cookies).
				SetHeader("X-XSRFToken", c.Xsrf).
				SetBody(map[string]interface{}{
					"type": cTemp.PathType,
					"path": cTemp.Path,
				}).SetResult(&jobResult).
				Put(createTempDirUrl)
			if err != nil {
				return err
			}
			if res.StatusCode() != http.StatusCreated {
				return fmt.Errorf("status code:" + res.Status())
			}

		}

	}
	return nil
}

func getJupyterBaseUrl(url string) string {
	jupyterUrlLength := len(url)
	baseUrl := url
	if strings.HasSuffix(url, "lab") {
		baseUrl = url[0 : jupyterUrlLength-len(path.Base(url))]
	}

	return baseUrl
}

func getRestyClient() *resty.Client {
	if restyClient == nil {
		restyClient = resty.New()
		restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
	}
	return restyClient
}
