package storage

import (
	"time"

	"github.com/juju/errors"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
)

/* ---------------- mysql storage ------------------*/

func (m *mysqlClient) createAPPProcess(ap *types.APPProcess) error {
	t := time.Now().Format(util.TimeFormat)
	ap.Ctime, ap.Utime = t, t

	tx, err := m.db.Beginx()
	if err != nil {
		return errors.Trace(err)
	}

	result, err := tx.NamedExec(appConfigInsert, ap.Config)
	if err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}

	ap.Config.ID, err = result.LastInsertId()
	if err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}

	result, err = tx.NamedExec(appProcessInsert, appProcessToPrams(ap))
	if err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}

	ap.ID, err = result.LastInsertId()
	if err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}

	if err := tx.Commit(); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (m *mysqlClient) updateAPPProcess(ap *types.APPProcess) error {
	t := time.Now().Format(util.TimeFormat)
	ap.Utime = t

	tx, err := m.db.Beginx()
	if err != nil {
		return errors.Trace(err)
	}

	var pre types.APPProcess
	if err := tx.Get(&pre, appProcessSelectByID, ap.ID); err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}
	ap.Config.ID = pre.Config.ID

	if _, err := tx.NamedExec(appConfigUpdate, ap.Config); err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}

	if _, err := tx.NamedExec(appProcessUpdate, appProcessToPrams(ap)); err != nil {
		tx.Rollback()
		return errors.Trace(err)
	}

	if err := tx.Commit(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (m *mysqlClient) listAPPProcess() ([]*types.APPProcess, error) {
	var apps []*types.APPProcess
	rows, err := m.db.Queryx(appProcessSelect)
	if err != nil {
		return nil, errors.Trace(err)
	}
	for rows.Next() {
		var app types.APPProcess
		if err := rows.StructScan(&app); err != nil {
			return nil, errors.Trace(err)
		}
		apps = append(apps, &app)
	}

	return apps, nil
}

func (m *mysqlClient) getAPPProcessByID(id int64) (*types.APPProcess, error) {
	var ap types.APPProcess
	if err := m.db.Get(&ap, appProcessSelectByID, id); err != nil {
		return nil, errors.Trace(err)
	}
	return &ap, nil
}

func (m *mysqlClient) getAPPProcessByExperimentID(id int64) ([]*types.APPProcess, error) {
	var apps []*types.APPProcess
	rows, err := m.db.Queryx(appProcessSelectByExperimentID, id)
	if err != nil {
		return nil, errors.Trace(err)
	}
	for rows.Next() {
		var app types.APPProcess
		if err := rows.StructScan(&app); err != nil {
			return nil, errors.Trace(err)
		}
		apps = append(apps, &app)
	}

	return apps, nil
}

func (m *mysqlClient) getAPPProcessByNamespaceAndName(namespace, name string) (*types.APPProcess, error) {
	var ap types.APPProcess
	if err := m.db.Get(&ap, appProcessSelectByNamespaceAndName, namespace, name); err != nil {
		return nil, errors.Trace(err)
	}
	return &ap, nil
}

func appProcessToPrams(ap *types.APPProcess) map[string]interface{} {
	return map[string]interface{}{
		"id":          ap.ID,
		"name":        ap.Name,
		"config_id":   ap.Config.ID,
		"report":      ap.Report,
		"create_time": ap.Ctime,
		"update_time": ap.Utime,
		"status":      ap.Status,
		"mark":        ap.Mark,
		"namespace":   ap.Namespace,
	}
}

const appConfigInsert = `
INSERT INTO app_config (
 name
,type
,namespace
,version
,label
,test_id
,experiment_id
,box_id
,binary_name
,url
,args
,resource_requirement
,host
,container_ids
,port
,envs
,actions
,image
) VALUES (
 :name
,:type
,:namespace
,:version
,:label
,:test_id
,:experiment_id
,:box_id
,:binary_name
,:url
,:args
,:resource_requirement
,:host
,:container_ids
,:port
,:envs
,:actions
,:image
)
`

const appConfigUpdate = `
UPDATE app_config SET
 name = :name
,type = :type
,namespace = :namespace
,version = :version
,label = :label
,test_id = :test_id
,experiment_id = :experiment_id
,box_id = :box_id
,binary_name = :binary_name
,url = :url
,args = :args
,resource_requirement = :resource_requirement
,host = :host
,container_ids = :container_ids
,port = :port
,envs = :envs
,actions = :actions
,image = :image
WHERE id = :id
`

const appProcessInsert = `
INSERT INTO app_process (
 name
,config_id
,report
,create_time
,update_time
,status
,mark
,namespace
) VALUES (
 :name
,:config_id
,:report
,:create_time
,:update_time
,:status
,:mark
,:namespace
)
`

const appProcessUpdate = `
UPDATE app_process SET
 name = :name
,config_id = :config_id
,report = :report
,update_time = :update_time
,status = :status
,mark = :mark
,namespace = :namespace
WHERE id = :id
`

const appProcessSelect = `
SELECT
 app_process.id
,app_process.name
,app_process.report
,app_process.create_time
,app_process.update_time
,app_process.status
,app_process.mark
,app_process.namespace
,app_config.id "app_config.id"
,app_config.name "app_config.name"
,app_config.type "app_config.type"
,app_config.namespace "app_config.namespace"
,app_config.version "app_config.version"
,app_config.label "app_config.label"
,app_config.test_id "app_config.test_id"
,app_config.experiment_id "app_config.experiment_id"
,app_config.box_id "app_config.box_id"
,app_config.binary_name "app_config.binary_name"
,app_config.url "app_config.url"
,app_config.args "app_config.args"
,app_config.resource_requirement "app_config.resource_requirement"
,app_config.host "app_config.host"
,app_config.container_ids "app_config.container_ids"
,app_config.port "app_config.port"
,app_config.envs "app_config.envs"
,app_config.actions "app_config.actions"
,app_config.image "app_config.image"
FROM app_process
JOIN app_config
WHERE app_process.config_id = app_config.id
`

const appProcessSelectByID = `
SELECT
 app_process.id
,app_process.name
,app_process.report
,app_process.create_time
,app_process.update_time
,app_process.status
,app_process.mark
,app_process.namespace
,app_config.id "app_config.id"
,app_config.name "app_config.name"
,app_config.type "app_config.type"
,app_config.namespace "app_config.namespace"
,app_config.version "app_config.version"
,app_config.label "app_config.label"
,app_config.test_id "app_config.test_id"
,app_config.experiment_id "app_config.experiment_id"
,app_config.box_id "app_config.box_id"
,app_config.binary_name "app_config.binary_name"
,app_config.url "app_config.url"
,app_config.args "app_config.args"
,app_config.resource_requirement "app_config.resource_requirement"
,app_config.host "app_config.host"
,app_config.container_ids "app_config.container_ids"
,app_config.port "app_config.port"
,app_config.envs "app_config.envs"
,app_config.actions "app_config.actions"
,app_config.image "app_config.image"
FROM app_process
JOIN app_config
WHERE app_process.config_id = app_config.id AND app_process.id = ?
`

const appProcessSelectByExperimentID = `
SELECT
 app_process.id
,app_process.name
,app_process.report
,app_process.create_time
,app_process.update_time
,app_process.status
,app_process.mark
,app_process.namespace
,app_config.id "app_config.id"
,app_config.name "app_config.name"
,app_config.type "app_config.type"
,app_config.namespace "app_config.namespace"
,app_config.version "app_config.version"
,app_config.label "app_config.label"
,app_config.test_id "app_config.test_id"
,app_config.experiment_id "app_config.experiment_id"
,app_config.box_id "app_config.box_id"
,app_config.binary_name "app_config.binary_name"
,app_config.url "app_config.url"
,app_config.args "app_config.args"
,app_config.resource_requirement "app_config.resource_requirement"
,app_config.host "app_config.host"
,app_config.container_ids "app_config.container_ids"
,app_config.port "app_config.port"
,app_config.envs "app_config.envs"
,app_config.actions "app_config.actions"
,app_config.image "app_config.image"
FROM app_process
JOIN app_config
WHERE app_process.config_id = app_config.id AND app_config.experiment_id = ?
`

const appProcessSelectByNamespaceAndName = `
SELECT
 app_process.id
,app_process.name
,app_process.report
,app_process.create_time
,app_process.update_time
,app_process.status
,app_process.mark
,app_process.namespace
,app_config.id "app_config.id"
,app_config.name "app_config.name"
,app_config.type "app_config.type"
,app_config.namespace "app_config.namespace"
,app_config.version "app_config.version"
,app_config.label "app_config.label"
,app_config.test_id "app_config.test_id"
,app_config.experiment_id "app_config.experiment_id"
,app_config.box_id "app_config.box_id"
,app_config.binary_name "app_config.binary_name"
,app_config.url "app_config.url"
,app_config.args "app_config.args"
,app_config.resource_requirement "app_config.resource_requirement"
,app_config.host "app_config.host"
,app_config.container_ids "app_config.container_ids"
,app_config.port "app_config.port"
,app_config.envs "app_config.envs"
,app_config.actions "app_config.actions"
,app_config.image "app_config.image"
FROM app_process
JOIN app_config
WHERE app_process.config_id = app_config.id AND app_process.namespace = ? AND app_process.name = ?
`
