package svctaskrunhandler

import (
	"context"
	"encoding/json"
	"testing"

	"github.com/pingcap/test-infra/common/model"
	reqM "github.com/pingcap/test-infra/common/model/tcms/request"
	taskM "github.com/pingcap/test-infra/common/model/tcms/taskruns"
	"github.com/stretchr/testify/require"
)

func TestRenderOneShot(t *testing.T) {
	act, err := renderOneShot(*sampleTaskRun(), *sampleTask())
	require.NoError(t, err)
	equalExpectedPlan(t, expectedOneShot(), act)
}

func sampleTask() *taskM.SvcTask {
	js := `
{
  "apiVersion": "api/v1",
  "kind": "SvcTask",
  "name": "ticdc",
  "metadata":{"id":2},
  "inputs": [
    {
      "name": "ghprbActualCommit"
    },
    {
      "name": "TIKV_IMAGE_TAG"
    },
    {
      "name": "PD_IMAGE_TAG"
    },
    {
      "name": "TIDB_IMAGE_TAG"
    }
  ],
  "outputs": [
    {
      "name": "@exitCode"
    }
  ],
  "implementation": {
    "plan": {
      "spec": {
        "tags": [
          "cdc"
        ],
        "testbed": {
          "generateName": "cdc-testbed-",
          "reclaimPolicy": {
            "gcStrategy": {
              "onFailure": {
                "artifact": {
                  "monitor": true,
                  "log": true
                },
                "reserveEnv": {
                  "durationOnWorkingHour": "0h"
                },
                "notification": {
                  "feishu": "https://open.feishu.cn/open-apis/bot/v2/hook/4f6c6406-c963-4d52-8641-cca17aa734e9"
                }
              }
            }
          },
          "items": [
            {
              "name": "upstream",
              "type": "TIDB_CLUSTER",
              "spec": {
                "imagePullPolicy": "Always",
                "pd": {
                  "replicas": 1,
                  "requests": {
                    "cpu": "2000m",
                    "memory": "8Gi"
                  },
                  "limits": {
                    "cpu": "2000m",
                    "memory": "8Gi"
                  },
                  "baseImage": "hub.pingcap.net/qa/pd",
                  "version": "{{inputs.PD_IMAGE_TAG}}"
                },
                "tikv": {
                  "storageClassName": "fast-disks",
                  "replicas": 1,
                  "requests": {
                    "cpu": "8000m",
                    "memory": "16Gi"
                  },
                  "limits": {
                    "cpu": "8000m",
                    "memory": "16Gi"
                  },
                  "baseImage": "hub.pingcap.net/qa/tikv",
                  "version": "{{inputs.TIKV_IMAGE_TAG}}"
                },
                "tidb": {
                  "replicas": 1,
                  "requests": {
                    "cpu": "8000m",
                    "memory": "16Gi"
                  },
                  "limits": {
                    "cpu": "8000m",
                    "memory": "16Gi"
                  },
                  "baseImage": "hub.pingcap.net/qa/tidb",
                  "version": "{{inputs.TIDB_IMAGE_TAG}}"
                },
                "ticdc": {
                  "replicas": 3,
                  "requests": {
                    "cpu": "4000m",
                    "memory": "8Gi"
                  },
                  "limits": {
                    "cpu": "8000m",
                    "memory": "16Gi"
                  },
                  "baseImage": "hub.pingcap.net/qa/ticdc",
                  "version": "{{inputs.ghprbActualCommit}}"
                },
                "version": "master"
              }
            },
            {
              "name": "downstream",
              "type": "MYSQL_NODE",
              "spec": {
                "image": "registry-mirror.pingcap.net/mysql/mysql-server:8.0.27-1.2.6-server",
                "port": 3306,
                "env": {
                  "MYSQL_ALLOW_EMPTY_PASSWORD": "yes",
                  "MYSQL_ROOT_HOST": "%"
                },
                "resources": {
                  "requests": null,
                  "memory": "4Gi",
                  "cpu": "4000m",
                  "storage": "500Gi"
                },
                "volumeMount": {
                  "mountPath": "/var/lib/mysql"
                }
              }
            },
            {
              "name": "workload",
              "type": "WORKLOAD_NODE",
              "spec": {
                "container": {
                  "name": "cdcboundaryworkload",
                  "image": "hub.pingcap.net/qa/cdc-boundary-workload",
                  "command": [
                    "tail",
                    "-f",
                    "/dev/null"
                  ],
                  "resources": {
                    "requests": {
                      "memory": "4Gi",
                      "cpu": "4000m"
                    }
                  }
                }
              }
            }
          ]
        },
        "steps": [
          {
            "name": "cdc-boundary-test-sync",
            "caseName": "cdc_boundary_test_sync",
            "arguments": [
              {
                "name": "upstream",
                "value": "{{testbed.upstream}}"
              },
              {
                "name": "downstream",
                "value": "{{testbed.downstream}}"
              },
              {
                "name": "workload",
                "value": "{{testbed.workload}}"
              }
            ]
          }
        ],
        "failFast": false
      }
    }
  }
}`
	rt := &taskM.SvcTask{}
	err := json.Unmarshal([]byte(js), rt)
	if err != nil {
		panic(err)
	}
	return rt
}

func sampleTaskRun() *taskM.SvcTaskRun {
	runJson := `{
    "apiVersion": "api/v1",
    "kind": "SvcTaskRun",
    "metadata": {
        "id": 21,
        "createAt": "2022-07-07T10:13:37.727964Z",
        "updateAt": "2022-07-07T10:19:21.959059Z",
        "createByID": 0,
        "updateByID": 0
    },
    "spec": {
        "svcTaskName": "ticdc",
        "inputs": [
            {
                "name": "ghprbActualCommit",
                "value": "6a9296064f80f4322649123044f30cf837d0350a"
            },
            {
                "name": "TIKV_IMAGE_TAG",
                "value": "master"
            },
            {
                "name": "PD_IMAGE_TAG",
                "value": "master"
            },
            {
                "name": "TIDB_IMAGE_TAG",
                "value": "master"
            }
        ]
    }
}
`
	taskRun := &taskM.SvcTaskRun{}
	err := json.Unmarshal([]byte(runJson), taskRun)
	if err != nil {
		panic(err)
	}
	return taskRun
}

func samplePlanRefTask() *taskM.SvcTask {
	js := `
{
  "apiVersion": "api/v1",
  "kind": "SvcTask",
  "name": "tiflash-ci",
  "metadata":{"id":2},
  "inputs": [
    {
      "name": "commit"
    }
  ],
  "outputs": [
    {
      "name": "@exitCode"
    }
  ],
  "implementation": {
    "plan": {
      "planID": 12,
      "variables":[{"name":"tiflash-version", "value": "{{inputs.commit}}"}]
    }
  }
}`
	rt := &taskM.SvcTask{}
	err := json.Unmarshal([]byte(js), rt)
	if err != nil {
		panic(err)
	}
	return rt
}

func samplePlanRefTaskRun() *taskM.SvcTaskRun {
	runJson := `{
    "apiVersion": "api/v1",
    "kind": "SvcTaskRun",
    "metadata": {
        "id": 21,
        "createAt": "2022-07-07T10:13:37.727964Z",
        "updateAt": "2022-07-07T10:19:21.959059Z"
    },
    "spec": {
        "svcTaskName": "tiflash-ci",
        "inputs": [
            {
                "name": "commit",
                "value": "6a9296064f80"
            }
        ]
    }
}
`
	taskRun := &taskM.SvcTaskRun{}
	err := json.Unmarshal([]byte(runJson), taskRun)
	if err != nil {
		panic(err)
	}
	return taskRun
}

func expectedOneShot() string {
	return `
{
    "name": "",
    "testbed": {
        "generateName": "cdc-testbed-",
        "physicalResourceRequest": {
            "cpu": {},
            "memory": {}
        },
        "items": [
            {
                "name": "upstream",
                "type": "TIDB_CLUSTER",
                "spec": {
                    "imagePullPolicy": "Always",
                    "pd": {
                        "replicas": 1,
                        "requests": {
                            "cpu": "2000m",
                            "memory": "8Gi"
                        },
                        "limits": {
                            "cpu": "2000m",
                            "memory": "8Gi"
                        },
                        "baseImage": "hub.pingcap.net/qa/pd",
                        "version": "master"
                    },
                    "tikv": {
                        "storageClassName": "fast-disks",
                        "replicas": 1,
                        "requests": {
                            "cpu": "8000m",
                            "memory": "16Gi"
                        },
                        "limits": {
                            "cpu": "8000m",
                            "memory": "16Gi"
                        },
                        "baseImage": "hub.pingcap.net/qa/tikv",
                        "version": "master"
                    },
                    "tidb": {
                        "replicas": 1,
                        "requests": {
                            "cpu": "8000m",
                            "memory": "16Gi"
                        },
                        "limits": {
                            "cpu": "8000m",
                            "memory": "16Gi"
                        },
                        "baseImage": "hub.pingcap.net/qa/tidb",
                        "version": "master"
                    },
                    "ticdc": {
                        "replicas": 3,
                        "requests": {
                            "cpu": "4000m",
                            "memory": "8Gi"
                        },
                        "limits": {
                            "cpu": "8000m",
                            "memory": "16Gi"
                        },
                        "baseImage": "hub.pingcap.net/qa/ticdc",
                        "version": "6a9296064f80f4322649123044f30cf837d0350a"
                    },
                    "version": "master"
                }
            },
            {
                "name": "downstream",
                "type": "MYSQL_NODE",
                "spec": {
                    "image": "registry-mirror.pingcap.net/mysql/mysql-server:8.0.27-1.2.6-server",
                    "port": 3306,
                    "env": {
                        "MYSQL_ALLOW_EMPTY_PASSWORD": "yes",
                        "MYSQL_ROOT_HOST": "%"
                    },
                    "resources": {
                        "requests": null,
                        "memory": "4Gi",
                        "cpu": "4000m",
                        "storage": "500Gi"
                    },
                    "volumeMount": {
                        "mountPath": "/var/lib/mysql"
                    }
                }
            },
            {
                "name": "workload",
                "type": "WORKLOAD_NODE",
                "spec": {
                    "container": {
                        "name": "cdcboundaryworkload",
                        "image": "hub.pingcap.net/qa/cdc-boundary-workload",
                        "command": [
                            "tail",
                            "-f",
                            "/dev/null"
                        ],
                        "resources": {
                            "requests": {
                                "memory": "4Gi",
                                "cpu": "4000m"
                            }
                        }
                    }
                }
            }
        ],
        "reclaimPolicy": {
            "lease": "",
            "gcStrategy": {
                "onFailure": {
                    "artifact": {
                        "monitor": true,
                        "log": true
                    },
                    "reserveEnv": {
                        "durationOnWorkingHour": "0h",
                        "scaleIn": false,
                        "scaleDown": false
                    },
                    "notification": {
                        "feishu": "https://open.feishu.cn/open-apis/bot/v2/hook/4f6c6406-c963-4d52-8641-cca17aa734e9"
                    }
                }
            }
        }
    },
    "steps": [
        {
            "name": "cdc-boundary-test-sync",
            "seq": 0,
            "timeout": 0,
            "caseName": "cdc_boundary_test_sync",
            "arguments": [
                {
                    "name": "upstream",
                    "value": "{{testbed.upstream}}"
                },
                {
                    "name": "downstream",
                    "value": "{{testbed.downstream}}"
                },
                {
                    "name": "workload",
                    "value": "{{testbed.workload}}"
                }
            ]
        }
    ],
    "failFast": false,
    "type": "ONE_SHOT",
    "tags": [
        "cdc"
    ],
    "waitTimeout": 0
}`
}

func equalExpectedPlan(t *testing.T, expected string, act *reqM.PlanCreateRequest) {
	marshal, err := json.Marshal(*act)
	require.NoError(t, err)
	require.JSONEq(t, expectedOneShot(), string(marshal))
}

type planExecRepoStub struct {
	plan   *reqM.PlanCreateRequest
	exec   model.TestPlanExecution
	execID model.ID
	err    error
}

func (p planExecRepoStub) Get(ctx context.Context, execID model.ID) (*model.TestPlanExecution, error) {
	if p.err != nil {
		return nil, p.err
	}
	return &p.exec, nil
}

func (p *planExecRepoStub) CreateOneShotPlan(ctx context.Context, plan *reqM.PlanCreateRequest) (model.ID, error) {
	p.plan = plan
	return p.execID, nil
}

func (p *planExecRepoStub) CreateExecTx(ctx context.Context, exec model.TestPlanExecution) (model.ID, error) {
	p.exec = exec
	return p.execID, nil
}

func TestPlanHandler_OnCreate(t *testing.T) {
	var execID model.ID = 1
	stub := &planExecRepoStub{
		execID: execID,
	}
	h := planHandler{
		taskClient: SvcTaskRefServiceStub{task: sampleTask()},
		planExec:   stub,
	}
	create, err := h.OnCreate(context.TODO(), sampleTaskRun())
	require.NoError(t, err)
	require.Equal(t, execID, create.Status.Implementation.Plan.ExecID)
	equalExpectedPlan(t, expectedOneShot(), stub.plan)
}

func TestPlanHandler_OnCreate_PlanRef(t *testing.T) {
	var execID model.ID = 1
	stub := &planExecRepoStub{
		execID: execID,
	}
	h := planHandler{
		taskClient: SvcTaskRefServiceStub{task: samplePlanRefTask()},
		planExec:   stub,
	}
	create, err := h.OnCreate(context.TODO(), samplePlanRefTaskRun())
	require.NoError(t, err)
	require.Equal(t, execID, create.Status.Implementation.Plan.ExecID)
	require.Equal(t, "6a9296064f80", stub.exec.VariableBinds[0].Value)
}

func TestPlanHandler_OnSync(t *testing.T) {
	var execID model.ID = 1
	stub := &planExecRepoStub{
		exec: model.TestPlanExecution{ID: execID, Status: model.ExecutionStatusSuccess},
	}
	h := planHandler{
		taskClient: SvcTaskRefServiceStub{task: sampleTask()},
		planExec:   stub,
	}

	run := sampleTaskRun()
	run.Status.Implementation.Plan = &taskM.PlanExecRef{ExecID: execID}
	synced, err := h.OnSync(context.TODO(), run)
	require.NoError(t, err)
	require.True(t, synced.Status.Completed)
	require.Equal(t, "0", synced.Status.Outputs[0].Value)
}
