package common

import (
	"archive/zip"
	"dbweb/core"
	"dbweb/lib/export"
	"dbweb/lib/safe"
	"dbweb/modules/common/recordview"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/linlexing/dbx/ddb"

	"github.com/linlexing/dbx/scan"

	"github.com/linlexing/dbx/schema"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/simplifiedchinese"
)

//Export 业务类
type Export struct{}
type exportField struct {
	Name      string
	Type      schema.DataType
	MaxLength int
}
type exportParam struct {
	Format       string
	Encoding     encoding.Encoding `json:"-" yaml:"-"`
	TypeTable    string
	TypeColumns  []*scan.ColumnType
	DataDbName   string
	DataSQL      string
	UniqueField  []string
	OutTableName string
	OutDbName    string
}

func (e *exportParam) JSON() string {
	bys, err := json.MarshalIndent(e, "", "\t")
	if err != nil {

		core.LOG.Panic(err)
	}
	return string(bys)
}
func init() {
	core.RegisterFun("export", new(Export), "_layout/blank")
}

//按照指定格式导出一批数据
func exportData(t *core.TaskRun) string {
	var err error
	p := t.Param.(exportParam)

	var exports export.Exports
	var fileName string
	switch p.Format {
	case "csv":
		fileName = p.TypeTable + ".csv"
		exports = new(export.ExportCsv)
	case "txt":
		fileName = p.TypeTable + ".txt"
		exports = new(export.ExportTxt)
	case "json":
		fileName = p.TypeTable + ".json"
		exports = new(export.ExportJson)
	case "excel":
		fileName = p.TypeTable + ".xls"
		exports = new(export.ExportExcel)
	case "sqlite3":
		fileName = p.TypeTable + ".sl3"
	}
	if exports != nil || p.Format == "sqlite3" {
		zipW := t.NeedZip()
		defer func() {
			if err = zipW.Close(); err != nil {
				core.LOG.Panic(err)
			}
		}()
		header := &zip.FileHeader{
			Name:   fileName,
			Flags:  1 << 11, // 使用utf8编码
			Method: zip.Deflate,
		}
		writeStream, err := zipW.CreateHeader(header)
		if err != nil {
			core.LOG.Panic(err)
		}
		dataDB := core.LoadOuterDB(t.Db, p.DataDbName)

		if exports != nil {
			core.LOG.Println("export not is sqlite3")

			if err = exports.Export(dataDB, p.TypeColumns, p.DataSQL, writeStream,
				p.Encoding, t.AddProgress); err != nil {
					core.LOG.Panic(err)
			}
		} else {
			core.LOG.WithFields(log.Fields{
				"DataDbName": p.DataDbName,
			}).Println("export is sqlite3")

			if err = export.ExportSqlite3(dataDB, p.TypeTable, p.TypeColumns, p.UniqueField,
				p.DataSQL, writeStream, t.AddProgress); err != nil {
				core.LOG.Panic(err)
			}
		}
	} else if p.Format == "mytable" {
		//当前数据库启动一个事务
		var tabName string
		if tabName, err = CreateMyTableAs(t.Db, p.OutTableName, p.DataDbName, p.OutDbName,
			p.TypeTable, p.TypeColumns, p.DataSQL, p.UniqueField, t.User, t.AddProgress); err != nil {
			core.LOG.Panic(err)
		}
		return fmt.Sprintf("导出完成，<a href='%s'>点击查看导出的数据</a>",
			t.User.Sign(fmt.Sprintf("/opentable/%s?db=%s", tabName,
				t.User.EncodeQueryValue(p.OutDbName))))
	}
	return ""
}

//Post 业务
func (e *Export) Post(p *core.ElementHandleArgs) {
	rparams := recordview.GetCallParam(p)

	if rparams == nil {
		p.RenderTimeout()
		return
	}
	fromElement := core.LoadElement(p.DB, rparams.FromElement)
	eleParam := &recordview.RecordViewParam{}
	if err := json.Unmarshal(fromElement.Params, eleParam); err != nil {
		core.LOG.Panic(err)
	}

	p.Req.ParseForm()
	selFields := []string{}
	for k := range p.Req.PostForm {
		names := strings.Split(k, "_")
		if len(names) > 1 {
			selFields = append(selFields, names[1])
		}
	}
	dataDB := core.LoadOuterDB(p.DB, rparams.Select.DBName)
	cols, err := ddb.Columns(dataDB, rparams.Select.DataSQL())
	if err != nil {
		core.LOG.Panic(err)
	}
	var strSQL = rparams.Select.DataOrderSQL()

	//如果列有变化，则重新生成sql
	if len(cols) != len(selFields) {
		cols = selFields
		strSQL = fmt.Sprintf("SELECT %s FROM (%s) exp_sql", strings.Join(selFields, ","), strSQL)
	}
	//重新生成TypeColumns
	typeCols := []*scan.ColumnType{}
	for _, one := range cols {
		bFound := false
		for _, col := range eleParam.AllColumns {
			if col.Name == one {
				bFound = true
				typeCols = append(typeCols, col)
				break
			}
		}
		if !bFound {
			typeCols = append(typeCols, &scan.ColumnType{Name: one, Type: schema.TypeString})
		}
	}
	expParam := exportParam{
		Format:       p.Req.PostFormValue("selformat"),
		TypeTable:    eleParam.TableName,
		TypeColumns:  typeCols,
		DataDbName:   rparams.Select.DBName,
		DataSQL:      strSQL,
		OutDbName:    p.Req.PostFormValue("seldb"),
		OutTableName: p.Req.PostFormValue("tablename"),
		UniqueField:  eleParam.UniqueField,
	}
	switch p.Req.PostFormValue("selencoding") {
	case "utf8":
		expParam.Encoding = nil
	case "gbk":
		expParam.Encoding = simplifiedchinese.GBK
	}
	task := &core.TaskRun{
		Db:       p.DB,
		Name:     fmt.Sprintf("%s 导出[%s]", fromElement.DisplayLabel(), expParam.Format),
		User:     p.User,
		ClientIP: p.Req.RemoteAddr,
		Param:    expParam,
		Func:     exportData,
	}
	if err := task.GoRun(); err != nil {
		p.RenderError(err.Error())

	} else {
		p.RenderMessage(fmt.Sprintf("导出操作将在后台运行，可以<a href='%s' target='_blank'>点击查看进度</a>", p.User.Sign("/browsetask/"+task.ID())))
	}
}

//Get 业务
func (e *Export) Get(p *core.ElementHandleArgs) {
	rparams := recordview.GetCallParam(p)

	if rparams == nil {
		p.RenderTimeout()
		return
	}
	dataDB := core.LoadOuterDB(p.DB, rparams.Select.DBName)
	fromElement := core.LoadElement(p.DB, rparams.FromElement)
	eleParam := &recordview.RecordViewParam{}
	if err := json.Unmarshal(fromElement.Params, eleParam); err != nil {
		core.LOG.Panic(err)
	}
	var vCount int64
	strSQL := fmt.Sprintf("select count(*) from (%s) exp_sql", rparams.Select.DataSQL())
	if err := dataDB.QueryRow(strSQL).Scan(&vCount); err != nil {
		fmt.Println(strSQL)
		core.LOG.Panic(err)
	}
	cols, err := ddb.Columns(dataDB, rparams.Select.DataSQL())
	if err != nil {
		core.LOG.Panic(err)
	}
	var tab *schema.Table
	if len(eleParam.TableName) > 0 {
		tab, err = schema.Find(dataDB.DriverName()).OpenTable(dataDB, eleParam.TableName)
		if err != nil {
			core.LOG.Panic(err)
		}
	}
	Fields := []*exportField{}
	for _, col := range cols {
		field := &exportField{
			Name:      col,
			Type:      schema.TypeString,
			MaxLength: -1,
		}
		//闭包的目的是简化代码，后一句append不能跳过
		func() {
			if tab != nil {
				if c := tab.ColumnByName(col); c != nil {
					field.Type = c.Type
					field.MaxLength = c.MaxLength
					return
				}
			}
			if c1 := eleParam.FindColumn(col); c1 != nil {
				field.Type = c1.Type
			}
		}()

		Fields = append(Fields, field)
	}

	names := []string{}
	dbs := core.OuterDBList(p.DB)
	for _, v := range dbs {
		if v.MyTable {
			names = append(names, v.Name)
		}
	}
	p.More["DefaultTableName"] = fmt.Sprintf("%s_%s", fromElement.DisplayLabel(), time.Now().Format("060102150405"))
	p.More["DBNames"] = names
	p.More["FromElement"] = fromElement
	p.More["RowCount"] = safe.Int(vCount)
	p.More["Fields"] = Fields
	p.HTML()
}
