package file_export

import (
	"encoding/json"
	"net/http"
	"os"
	"path"
	"path/filepath"

	"be.ems/lib/file"
	"be.ems/lib/log"
	"be.ems/lib/services"
	"be.ems/src/framework/config"
	"be.ems/src/framework/datasource"
	"be.ems/src/framework/i18n"
	"be.ems/src/framework/ssh"
	"be.ems/src/framework/utils/crypto"
	"be.ems/src/framework/utils/ctx"
	"be.ems/src/framework/vo/result"
	systemService "be.ems/src/modules/system/service"
	"github.com/gin-gonic/gin"
)

type SysJobResponse struct {
	SysJob
	TableName    string `json:"tableName"`
	TableDisplay string `json:"tableDisplay"`
	FilePath     string `json:"filePath"`
}

type TargetParams struct {
	Duration  int    `json:"duration"`
	TableName string `json:"tableName"`
	Columns   string `json:"columns"`  // exported column name of time string
	TimeCol   string `json:"timeCol"`  // time stamp of column name
	TimeUnit  string `json:"timeUnit"` // timestamp unit: second/micro/milli
	Extras    string `json:"extras"`   // extras condition for where
	FilePath  string `json:"filePath"` // file path
}

func (m *SysJob) GetFileExportTable(c *gin.Context) {
	var results []SysJob

	err := datasource.DefaultDB().Table(m.TableName()).Where("invoke_target=? and status=1", INVOKE_FILE_EXPORT).
		Find(&results).Error
	if err != nil {
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}
	language := ctx.AcceptLanguage(c)
	var response []SysJobResponse
	for _, job := range results {
		var params TargetParams
		if err := json.Unmarshal([]byte(job.TargetParams), &params); err != nil {
			c.JSON(http.StatusOK, services.ErrResp(err.Error()))
			return
		}
		TableDisplay := i18n.TKey(language, "table."+params.TableName)
		if TableDisplay == "" {
			TableDisplay = params.TableName
		}
		response = append(response, SysJobResponse{
			SysJob:       job,
			TableName:    params.TableName,
			TableDisplay: TableDisplay,
			FilePath:     params.FilePath,
		})
	}
	c.JSON(http.StatusOK, services.DataResp(response))
}

func (m *FileExport) GetFileList(c *gin.Context) {
	var querys FileExportQuery

	if err := c.ShouldBindQuery(&querys); err != nil {
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}

	files, err := file.GetFileInfo(querys.Path, querys.Suffix)
	if err != nil {
		log.Error("failed to GetFileInfo:", err)
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}

	// split files list
	lenNum := int64(len(files))
	start := (querys.PageNum - 1) * querys.PageSize
	end := start + querys.PageSize
	var splitList []file.FileInfo
	if start >= lenNum {
		splitList = []file.FileInfo{}
	} else if end >= lenNum {
		splitList = files[start:]
	} else {
		splitList = files[start:end]
	}
	total := len(files)
	c.JSON(http.StatusOK, services.TotalDataResp(splitList, total))
}

func (m *FileExport) Total(c *gin.Context) {
	dir := c.Query("path")

	fileCount, dirCount, err := file.GetFileAndDirCount(dir)
	if err != nil {
		log.Error("failed to GetFileAndDirCount:", err)
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}
	total := fileCount + dirCount
	c.JSON(http.StatusOK, services.TotalResp(int64(total)))
}

func (m *FileExport) DownloadHandler(c *gin.Context) {
	dir := c.Query("path")
	fileName := c.Param("fileName")
	filePath := filepath.Join(dir, fileName)

	file, err := os.Open(filePath)
	if err != nil {
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}
	defer file.Close()

	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}

	c.Header("Content-Disposition", "attachment; filename="+fileName)
	c.Header("Content-Type", "application/octet-stream")
	c.File(filePath)
}

func (m *FileExport) Delete(c *gin.Context) {
	fileName := c.Param("fileName")
	dir := c.Query("path")
	filePath := filepath.Join(dir, fileName)

	if err := os.Remove(filePath); err != nil {
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}
	c.JSON(http.StatusNoContent, nil) // 204 No Content
}

// 设置FTP配置
// POST /table/ftp
func (m *SysJob) SetFTPConfig(c *gin.Context) {
	language := ctx.AcceptLanguage(c)
	var body struct {
		Password string `json:"password" `
		Username string `json:"username" binding:"required"`
		ToIp     string `json:"toIp" binding:"required"`
		ToPort   int64  `json:"toPort" binding:"required"`
		Enable   bool   `json:"enable"`
		Dir      string `json:"dir" binding:"required"`
	}
	if err := c.ShouldBindBodyWithJSON(&body); err != nil {
		c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
		return
	}

	// 获取配置
	cfg := systemService.NewSysConfigImpl.SelectConfigByKey("sys.exportTable")
	if cfg.ConfigID != "" {
		// 加密body
		appKey := config.Get("aes.appKey").(string)
		byteData, err := json.Marshal(body)
		if err != nil {
			c.JSON(200, result.ErrMsg(err.Error()))
			return
		}
		bodyEn, err := crypto.AESEncryptBase64(string(byteData), appKey)
		if err != nil {
			c.JSON(200, result.ErrMsg(err.Error()))
			return
		}
		// 更新
		cfg.ConfigValue = bodyEn
		systemService.NewSysConfigImpl.UpdateConfig(cfg)
	}

	c.JSON(200, result.Ok(nil))
}

// 获取FTP配置
// GET /table/ftp
func (m *SysJob) GetFTPConfig(c *gin.Context) {
	// 获取配置
	cfg := systemService.NewSysConfigImpl.SelectConfigByKey("sys.exportTable")
	if cfg.ConfigID != "" {
		// 解密body
		appKey := config.Get("aes.appKey").(string)
		bodyDe, err := crypto.AESDecryptBase64(cfg.ConfigValue, appKey)
		if err != nil {
			c.JSON(200, result.ErrMsg(err.Error()))
			return
		}
		var body struct {
			Password string `json:"password" `
			Username string `json:"username" binding:"required"`
			ToIp     string `json:"toIp" binding:"required"`
			ToPort   int64  `json:"toPort" binding:"required"`
			Enable   bool   `json:"enable"`
			Dir      string `json:"dir" binding:"required"`
		}
		err = json.Unmarshal([]byte(bodyDe), &body)
		if err != nil {
			c.JSON(200, result.ErrMsg(err.Error()))
			return
		}
		c.JSON(200, result.OkData(body))
		return
	}

	c.JSON(200, result.Ok(nil))
}

// FTP发送
// PUT /table/ftp
func (m *SysJob) PutFTP(c *gin.Context) {
	language := ctx.AcceptLanguage(c)
	var body struct {
		FilePath string `json:"filePath" binding:"required"`
		FileName string `json:"fileName" binding:"required"`
	}
	if err := c.ShouldBindBodyWithJSON(&body); err != nil {
		c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
		return
	}

	localFilePath := filepath.Join(body.FilePath, body.FileName)

	// 判断文件是否存在
	if _, err := os.Stat(localFilePath); os.IsNotExist(err) {
		c.JSON(200, result.ErrMsg(err.Error()))
		return
	}

	// 获取配置
	var cfgData struct {
		Password string `json:"password" `
		Username string `json:"username" binding:"required"`
		ToIp     string `json:"toIp" binding:"required"`
		ToPort   int64  `json:"toPort" binding:"required"`
		Enable   bool   `json:"enable"`
		Dir      string `json:"dir" binding:"required"`
	}
	cfg := systemService.NewSysConfigImpl.SelectConfigByKey("sys.exportTable")
	if cfg.ConfigID != "" {
		// 解密body
		appKey := config.Get("aes.appKey").(string)
		bodyDe, err := crypto.AESDecryptBase64(cfg.ConfigValue, appKey)
		if err != nil {
			c.JSON(200, result.ErrMsg(err.Error()))
			return
		}
		err = json.Unmarshal([]byte(bodyDe), &cfgData)
		if err != nil {
			c.JSON(200, result.ErrMsg(err.Error()))
			return
		}
	}
	if !cfgData.Enable {
		c.JSON(200, result.ErrMsg("Setting Remote Backup is disabled"))
		return
	}

	connSSH := ssh.ConnSSH{
		User:     cfgData.Username,
		Password: cfgData.Password,
		Addr:     cfgData.ToIp,
		Port:     cfgData.ToPort,
		AuthMode: "0",
	}
	sshClient, err := connSSH.NewClient()
	if err != nil {
		c.JSON(200, result.ErrMsg(err.Error()))
		return
	}
	defer sshClient.Close()
	// 网元主机的SSH客户端进行文件传输
	sftpClient, err := sshClient.NewClientSFTP()
	if err != nil {
		c.JSON(200, result.ErrMsg(err.Error()))
		return
	}
	defer sftpClient.Close()
	// 远程文件
	remotePath := filepath.Join(cfgData.Dir, path.Base(body.FilePath), body.FileName)
	// 复制到远程
	if err = sftpClient.CopyFileLocalToRemote(localFilePath, remotePath); err != nil {
		c.JSON(200, result.ErrMsg("error uploading file"))
		return
	}
	c.JSON(200, result.Ok(nil))
}
