package nbi_file

import (
	"archive/zip"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"be.ems/lib/dborm"
	"be.ems/lib/file"
	"be.ems/lib/global"
	"be.ems/lib/log"
	"be.ems/lib/services"
	"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 *FileNBI) GetFileList(c *gin.Context) {
	var querys FileNBIQuery

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

	if querys.Path == "" {
		tableName := ""
		ok := false
		switch querys.Category {
		case "cdr":
			tableName, ok = CDRTableMapper[querys.Type]
			if tableName == "" || !ok {
				c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid CDR file type: %s", querys.Type)))
				return
			}
		case "log":
			tableName, ok = LogTableMapper[querys.Type]
			if tableName == "" || !ok {
				c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid log file type: %s", querys.Type)))
				return
			}
		default:
			c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid log file category: %s", querys.Category)))
			return
		}

		s := SysJob{}
		where := fmt.Sprintf("invoke_target='%s' and status=1 and JSON_UNQUOTE(JSON_EXTRACT(target_params,'$.tableName'))='%s'", INVOKE_FILE_EXPORT, tableName)
		_, err := dborm.XEngDB().Table(s.TableName()).
			Select("JSON_UNQUOTE(JSON_EXTRACT(target_params, '$.filePath')) as file_path").
			Where(where).
			Get(&querys.Path)
		if 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 *FileNBI) 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 *FileNBI) GetSingleFileHandler(c *gin.Context) {
	var querys FileNBIQuery

	querys.Category = c.Param("category")
	querys.Type = c.Param("type")
	querys.DateIndex = c.Param("dateIndex")
	if err := c.ShouldBindQuery(&querys); err != nil {
		c.JSON(http.StatusOK, services.ErrResp(err.Error()))
		return
	}

	tableName := ""
	if querys.Path == "" {
		ok := false
		switch querys.Category {
		case "cdr":
			tableName, ok = CDRTableMapper[querys.Type]
			if tableName == "" || !ok {
				c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid CDR file type: %s", querys.Type)))
				return
			}
		case "log":
			tableName, ok = LogTableMapper[querys.Type]
			if tableName == "" || !ok {
				c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid log file type: %s", querys.Type)))
				return
			}
		default:
			c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid log file category: %s", querys.Category)))
			return
		}

		s := SysJob{}
		where := fmt.Sprintf("invoke_target='%s' and status=1 and JSON_UNQUOTE(JSON_EXTRACT(target_params,'$.tableName'))='%s'", INVOKE_FILE_EXPORT, tableName)
		_, err := dborm.XEngDB().Table(s.TableName()).
			Select("JSON_UNQUOTE(JSON_EXTRACT(target_params, '$.filePath')) as file_path").
			Where(where).
			Get(&querys.Path)
		if err != nil {
			c.JSON(http.StatusOK, services.ErrResp(err.Error()))
			return
		}
	}

	fileName := tableName + "_export_" + querys.DateIndex + "0000" + ".csv"
	filePath := filepath.Join(querys.Path, 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 *FileNBI) GetMultiFileHandler(c *gin.Context) {
	var querys FileNBIQuery

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

	tableName := ""
	if querys.Path == "" {
		ok := false
		switch querys.Category {
		case "cdr":
			tableName, ok = CDRTableMapper[querys.Type]
			if tableName == "" || !ok {
				c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid CDR file type: %s", querys.Type)))
				return
			}
		case "log":
			tableName, ok = LogTableMapper[querys.Type]
			if tableName == "" || !ok {
				c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid log file type: %s", querys.Type)))
				return
			}
		default:
			c.JSON(http.StatusOK, services.ErrResp(fmt.Sprintf("invalid log file category: %s", querys.Category)))
			return
		}

		s := SysJob{}
		where := fmt.Sprintf("invoke_target='%s' and status=1 and JSON_UNQUOTE(JSON_EXTRACT(target_params,'$.tableName'))='%s'", INVOKE_FILE_EXPORT, tableName)
		_, err := dborm.XEngDB().Table(s.TableName()).
			Select("JSON_UNQUOTE(JSON_EXTRACT(target_params, '$.filePath')) as file_path").
			Where(where).
			Get(&querys.Path)
		if err != nil {
			c.JSON(http.StatusOK, services.ErrResp(err.Error()))
			return
		}
	}

	zipWriter := zip.NewWriter(c.Writer)
	defer zipWriter.Close()

	for _, fileName := range querys.FileNames {
		filePath := filepath.Join(querys.Path, 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
		}
		writer, err := zipWriter.Create(filepath.Base(fileName))
		if err != nil {
			c.JSON(http.StatusOK, services.ErrResp(err.Error()))
			return
		}

		if _, err := io.Copy(writer, file); err != nil {
			c.JSON(http.StatusOK, services.ErrResp(err.Error()))
			return
		}

	}
	zipFile := tableName + "_export_" + time.Now().Local().Format(global.DateData) + ".zip"
	c.Header("Content-Disposition", "attachment; filename="+zipFile)
	c.Header("Content-Type", "application/zip")
	//c.File(filePath)
}
