package api

import (
	"encoding/json"

	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/ngm/performance_insight/compute_engine/index_advisor"
	"github.com/pingcap-inc/clinic/pkg/ngm/performance_insight/service"
)

type CreateTaskResponse struct {
	TaskID uint `json:"task_id" example:"1"`
}

type ListTaskResponse struct {
	Total uint64 `json:"total"`
	Tasks []Task `json:"tasks"`
}

type LatestTaskResponse struct {
	Latest         *Task `json:"latest"`
	LatestFinished *Task `json:"latest_finished"`
}

type Task struct {
	TaskID uint `json:"task_id" example:"1"`

	CreatedAt uint64 `json:"created_at" example:"1672992422"`
	UpdateAt  uint64 `json:"update_at" example:"1672992422"`

	ClusterID               uint   `json:"cluster_id" example:"1"`
	PerformInsightClusterID uint   `json:"perform_insight_cluster_id" example:"1"`
	DbaasClusterID          uint64 `json:"dbaas_cluster_id" example:"1"`
	DbaasOrgID              uint64 `json:"dbaas_org_id" example:"1"`
	Status                  string `json:"status" example:"succeeded" format:"string" enums:"created,running,succeeded,failed"`
	Type                    string `json:"type" example:"unstable_plan_insight" format:"string" enums:"unstable_plan_insight,index_insight,hint_insight"`
	LastFailedMessage       string `json:"last_failed_message" example:"failed to connect to cluster"`
	MaterialCount           uint   `json:"material_count" example:"1"`
	AdviseCount             int64  `json:"advise_count" example:"1"`
}

type SubmitMysqlAccountRequest struct {
	User     string `json:"user" example:"foo"`
	Password string `json:"password" example:"bar"`
}

type GetClinicPublicAddressResponse struct {
	Addresses []string `json:"addresses"`
}

type ListStatementsResponse struct {
	Total      uint64      `json:"total"`
	Statements []Statement `json:"statements"`
}

type Statement struct {
	StatementID uint `json:"statement_id" example:"1"`

	CreatedAt uint64 `json:"created_at" example:"1672992422"`
	UpdatedAt uint64 `json:"updated_at" example:"1672992422"`

	TaskCreatedAt   uint64 `json:"task_created_at" example:"1672992422"`
	TaskOffsetHours uint   `json:"task_offset_hours" example:"1"`

	StmtType        string `json:"stmt_type"`
	SchemaName      string `json:"schema_name"`
	Digest          string `json:"digest"`
	DigestText      string `json:"digest_text"`
	QuerySampleText string `json:"query_sample_text"`
	TableNames      string `json:"table_names"`
	IndexNames      string `json:"index_names"`
	SampleUser      string `json:"sample_user"`

	InsightType string `json:"insight_type" example:"unstable_plan" enums:"unstable_plan"`

	PlanCount                uint64  `json:"plan_count"`
	MinOfAvgLatenciesOfPlans uint64  `json:"min_of_avg_latencies_of_plans"`
	MaxOfAvgLatenciesOfPlans uint64  `json:"max_of_avg_latencies_of_plans"`
	M                        float64 `json:"m"`
	N                        float64 `json:"n"`

	HasSuggestedPlan    bool                 `json:"has_suggested_plan"`
	Impact              string               `json:"impact,omitempty" example:"HIGH" enums:"HIGH,MIDDLE,LOW"`
	SuggestPlanOverview *SuggestPlanOverview `json:"suggest_plan_overview,omitempty"`
}

func fromModelStatement(stmt *db.PlanInsightResultStatement, planOverview *SuggestPlanOverview) Statement {
	return Statement{
		StatementID:              stmt.ID,
		CreatedAt:                uint64(stmt.CreatedAt.Unix()),
		UpdatedAt:                uint64(stmt.UpdatedAt.Unix()),
		TaskCreatedAt:            uint64(stmt.PerformanceInsightTask.CreatedAt.Unix()),
		TaskOffsetHours:          stmt.PerformanceInsightTask.OffsetHours,
		StmtType:                 stmt.StmtType,
		SchemaName:               stmt.SchemaName,
		Digest:                   stmt.Digest,
		DigestText:               stmt.DigestText,
		QuerySampleText:          stmt.QuerySampleText,
		TableNames:               stmt.TableNames,
		IndexNames:               stmt.IndexNames,
		SampleUser:               stmt.SampleUser,
		PlanCount:                stmt.PlanCount,
		MinOfAvgLatenciesOfPlans: stmt.MinOfAvgLatenciesOfPlans,
		MaxOfAvgLatenciesOfPlans: stmt.MaxOfAvgLatenciesOfPlans,
		M:                        stmt.M,
		N:                        stmt.N,
		Impact:                   stmt.Impact,
		InsightType:              service.TaskTypeUnstablePlanInsight,
		SuggestPlanOverview:      planOverview,
		HasSuggestedPlan:         stmt.HasSuggestedPlan,
	}
}

type SuggestPlanOverview struct {
	PlanID                 uint   `json:"plan_id"`
	PlanDigest             string `json:"plan_digest"`
	CreateBindingStatement string `json:"create_binding_statement"`
	DropBindingStatement   string `json:"drop_binding_statement"`
}

type Plan struct {
	PlanID uint `json:"plan_id"`

	CreatedAt uint64 `json:"created_at" example:"1672992422"`
	UpdatedAt uint64 `json:"updated_at" example:"1672992422"`

	TaskCreatedAt   uint64 `json:"task_created_at" example:"1672992422"`
	TaskOffsetHours uint   `json:"task_offset_hours" example:"1"`

	PlanDigest string `json:"plan_digest"`
	ExecCount  uint64 `json:"exec_count"`
	AvgLatency uint64 `json:"avg_latency"`
	MinLatency uint64 `json:"min_latency"`
	MaxLatency uint64 `json:"max_latency"`
	AvgMemByte uint64 `json:"avg_mem_byte"`
	MaxMemByte uint64 `json:"max_mem_byte"`

	IsSuggested bool `json:"is_suggested"`
}

func fromModelPlan(plan *db.PlanInsightResultPlan, stmt *db.PlanInsightResultStatement) Plan {
	return Plan{
		PlanID:          plan.ID,
		CreatedAt:       uint64(plan.CreatedAt.Unix()),
		UpdatedAt:       uint64(plan.UpdatedAt.Unix()),
		TaskCreatedAt:   uint64(stmt.PerformanceInsightTask.CreatedAt.Unix()),
		TaskOffsetHours: stmt.PerformanceInsightTask.OffsetHours,
		PlanDigest:      plan.PlanDigest,
		ExecCount:       plan.ExecCount,
		AvgLatency:      plan.AvgLatency,
		MinLatency:      plan.MinLatency,
		MaxLatency:      plan.MaxLatency,
		AvgMemByte:      plan.AvgMemByte,
		MaxMemByte:      plan.MaxMemByte,
		IsSuggested:     plan.IsSuggested,
	}
}

type StatementDetails struct {
	Statement Statement `json:"statement"`
	Plans     []Plan    `json:"plans"`
}

type ListIndexAdvisesResponse struct {
	Count   int64                `json:"count"`
	Results []IndexAdvisorResult `json:"results"`
}

type IndexAdvisorResult struct {
	ID int `json:"id"`
	index_advisor.Result
}

func fromModelIndexAdvisorResult(r db.IndexAdvisorResult) IndexAdvisorResult {
	var result index_advisor.Result
	_ = json.Unmarshal([]byte(r.Result), &result)
	return IndexAdvisorResult{
		ID:     int(r.ID),
		Result: result,
	}
}
