package service

import (
	"context"
	"time"

	"github.com/pingcap/test-infra/common/model"
	"github.com/pingcap/test-infra/common/model/rms/response"
	reqM "github.com/pingcap/test-infra/common/model/tcms/request"
	respM "github.com/pingcap/test-infra/common/model/tcms/response"
	"github.com/pingcap/test-infra/common/model/tcms/taskruns"
	"github.com/pingcap/test-infra/services/tcms/service/common"
	serror "github.com/pingcap/test-infra/services/tcms/service/common/errors"
	"github.com/pingcap/test-infra/services/tcms/utils"
)

type SearchService interface {
	// AddDocuments add documents to search engine. `documents` must be a slice.
	AddDocuments(ctx utils.Context, index string, documents any) (updateID int64, err error)
	SearchCases(ctx utils.Context, query string, tags []string, offset, count uint) (data []interface{}, total int, err error)
	SearchPlans(ctx utils.Context, query string, ty model.TestPlanType, branch string, offset, count uint) (data []interface{}, total int, err error)
	SearchPlanExecutions(ctx utils.Context, query, email string, status *model.ExecutionStatus, triggerID model.ID, offset, count uint) ([]interface{}, int, error)
}

type GoogleService interface {
	ValidateIDToken(ctx context.Context, idToken string) (*common.OAuthResult, error)
}

type RMSRPCService interface {
	Ping(endpoint string) error
	GetResourcePoolStatus(endpoint, token, resourcePool string) (*response.GetResourcePoolStatusResponse, *serror.Error)
	GetTestbedResourceStatus(endpoint, token, resourcePool string) (*response.GetTestbedsResourceResponse, *serror.Error)
	ListTestBeds(RMSEndpoint, token string) (*response.ListTestbedResponse, *serror.Error)
	GetTestBed(testbedURL, token string, withResourceDetails bool) (*response.GetTestbedResponse, *serror.Error)
	RenewTestBed(lease, testbedURL, token string) *serror.Error
	ReleaseTestBed(testbedURL, token string) *serror.Error
	RestoreMonitor(testbed, clusterType, testbedURL, promTarballURL, token string) (*response.RestoreMonitorResponse, *serror.Error)
}

type UserService interface {
	AutoComplete(ctx utils.Context, email string, count int) ([]*model.User, error)
	GetByEmail(ctx utils.Context, email string) (*model.User, error)

	// ListBasicInfo returns users' (id, email, name, avatar).
	ListBasicInfo(ctx utils.Context, ids []model.ID, emails []string) ([]*model.User, error)
	Get(ctx utils.Context, id model.ID) (*model.User, error)
	Create(ctx utils.Context, user *model.User) (*model.User, error)
	Update(ctx utils.Context, user *model.User) (*model.User, error)
}

type TokenService interface {
	Validate(ctx utils.Context, token string) (*model.PersonalToken, error)
	List(ctx utils.Context, userID model.ID) ([]*model.PersonalToken, error)
	Create(ctx utils.Context, t *model.PersonalToken) (string, error)
	UpdateLastUsedAt(id model.ID) error
	Delete(ctx utils.Context, userID model.ID, ids []model.ID) ([]string, error)
}

type CaseService interface {
	ListBasicInfo(ctx utils.Context, ids []model.ID, names []string, excludeDeleted ...bool) ([]*model.TestCase, error)
	AutoComplete(ctx utils.Context, name string, count int) ([]*model.TestCase, error)

	// List returns cases ordered by `updated_at` desc.
	List(ctx utils.Context, opts common.CaseQueryOpts) ([]*model.TestCase, int, error)
	// If `historyOpts` is nil, returns the current version of the case.
	// If `historyOpts` is not nil, returns a history version of the case.
	Get(ctx utils.Context, id model.ID, historyOpts *common.HistoryGetOpts) (*model.TestCase, error)
	ListHistories(ctx utils.Context, id model.ID, opts common.HistoryListOpts) ([]model.HistoryRecord, error)
	Create(ctx utils.Context, c *reqM.CaseCreateRequest) (*model.TestCase, error)
	Update(ctx utils.Context, id model.ID, c *reqM.CaseUpdateRequest) (*model.TestCase, error)
	Delete(ctx utils.Context, id model.ID, hard ...bool) error
}

type PlanService interface {
	AllExist(ctx utils.Context, ids map[model.ID]struct{}) (bool, error)
	List(ctx utils.Context, opts common.PlanQueryOpts) ([]*model.TestPlan, int, error)
	AutoComplete(ctx utils.Context, ty model.TestPlanType, name string, count int) ([]*model.TestPlan, error)
	Get(ctx utils.Context, id model.ID) (*model.TestPlan, error)
	GetReleasePlans(ctx utils.Context, branch string, maxPlan ...int) (planIDs []model.ID, err error)
	Create(ctx utils.Context, plan *reqM.PlanCreateRequest) (*model.TestPlan, error)
	Delete(ctx utils.Context, id model.ID) error
}

type GitHubPlanService interface {
	// Upsert creates or updates a GitHub plan.
	Upsert(ctx utils.Context, p *reqM.PlanCreateRequest) (model.ID, error)
	// DeleteOlds deletes all old GitHub plans which belongs to the branch.
	DeleteOlds(ctx utils.Context, branch string) error
	// ListBranchNames returns branchs' name.
	ListBranchNames(ctx utils.Context, prefix string) ([]string, error)
	// ListBranchPlans returns plans' id and name of the branch.
	ListBranchPlans(ctx utils.Context, branch string, planNames []string) ([]model.IDAndName, error)
}

type TriggerService interface {
	// List returns triggers ordered by `updated_at` desc.
	List(ctx utils.Context, opts common.TriggerQueryOpts) ([]*model.TestTrigger, int, error)
	Trigger(ctx utils.Context, ty model.TriggerType, triggerAt time.Time, count int) (int, error)
	AutoComplete(ctx utils.Context, name string, ty model.TriggerType, count int) ([]*model.TestTrigger, error)
	Get(ctx utils.Context, id model.ID) (*model.TestTrigger, error)
	AddPlanIDs(ctx utils.Context, id model.ID, newPlanIDs []model.ID) error
	Create(ctx utils.Context, t *reqM.TriggerCreateRequest) (*model.TestTrigger, error)
	Delete(ctx utils.Context, id model.ID) error
}

type ExecutionService interface {
	GetStatus(ctx utils.Context, id model.ID) (*model.ExecutionStatus, error)
	UpdateNote(ctx utils.Context, id model.ID, note string) error
	UpdateReason(ctx utils.Context, id model.ID, reason model.ExecFailReason) error
}

type CaseExecutionService interface {
	ExecutionService

	// List returns case-executions ordered by `created_at` desc.
	List(ctx utils.Context, opts common.CaseExecQueryOpts) ([]*model.TestCaseExecution, int, error)
	Get(ctx utils.Context, id model.ID) (*model.TestCaseExecution, error)
	Create(ctx utils.Context, exec *reqM.CaseExecutionCreateRequest) (*model.TestCaseExecution, error)

	// Update overrides case-execution's started_at, completed_at, status and meta.
	Update(ctx utils.Context, id model.ID, exec *reqM.CaseExecutionUpdateRequest) (*model.TestCaseExecution, error)
}

type PlanExecutionService interface {
	ExecutionService
	List(ctx utils.Context, opts common.PlanExecQueryOpts) ([]*model.TestPlanExecution, int, error)
	// ListFailedSteps lists all failed steps of the plan-execution.
	ListFailedSteps(ctx utils.Context, id model.ID) (steps []string, err error)
	Get(ctx utils.Context, id model.ID) (*model.TestPlanExecution, error)

	// Update overrides plan-execution's started_at, completed_at, status and meta.
	Update(ctx utils.Context, id model.ID, exec *reqM.PlanExecutionUpdateRequest) (*model.TestPlanExecution, error)
	// Cancel cancels plan-execution.
	Cancel(ctx utils.Context, planID *model.ID, ids []model.ID) error
	// Rerun selects steps to rerun. If `steps` is empty, all steps will be rerun.
	Rerun(ctx utils.Context, id model.ID, steps []string) (execID model.ID, err error)
	// IsAllFinished checks if all the plan-executions which were created at `createdAt` are finished.
	IsAllFinished(ctx utils.Context, createdAt time.Time) (bool, error)
	CreateOneShotPlan(ctx utils.Context, plan *reqM.PlanCreateRequest) (p *model.TestPlan, e model.ID, err error)
}

type TriageService interface {
	ListPlanTriages(ctx utils.Context, opts common.PlanTriageQueryOpts) ([]*model.Triage, error)
	CreatePlanTriage(ctx utils.Context, triage *model.Triage) (*model.Triage, error)
	UpdatePlanTriage(ctx utils.Context, id model.ID, req *reqM.PlanTriageUpdateRequest) error
	GetPlanTriage(ctx utils.Context, id model.ID) (*model.Triage, error)
	ListCaseTriages(ctx utils.Context, opts common.CaseTriageQueryOpts) ([]*model.CaseTriage, error)
	CreateCaseTriage(ctx utils.Context, caseTriage *model.CaseTriage) (*model.CaseTriage, error)
	UpdateCaseTriage(ctx utils.Context, id model.ID, req *reqM.CaseTriageUpdateRequest) error
	GetCaseTriage(ctx utils.Context, id model.ID) (*model.CaseTriage, error)
	ForkCaseTriages(ctx utils.Context, id, forkFromID model.ID) (*model.CaseTriage, error)
	SuggestCaseTriages(ctx utils.Context, id model.ID) ([]*model.CaseTriage, error)
}

type TestbedService interface {
	List(ctx utils.Context, opts common.TestbedQueryOpts) ([]*model.TestBedInfo, int, error)
	Create(ctx utils.Context, testbed *model.TestBedInfo) (*model.TestBedInfo, error)
	Update(ctx utils.Context, opts common.TestbedUpdateOpts) error
	Get(ctx utils.Context, planExecID model.ID, testbedName string, withResourceDetails bool) (*model.TestBedInfo, error)
	Renew(ctx utils.Context, planExecID model.ID, testbedName string, lease string) error
	Release(ctx utils.Context, planExecID model.ID, testbedName string) error
	RestoreMonitor(ctx utils.Context, planExecID model.ID, testbedName, clusterType, promTarballURL string) (*model.TestBedInfo, error)
}

type SummaryService interface {
	DoDailySummary(ctx utils.Context, timeFromStartedAt time.Time, durationToStartedAt time.Duration) (*model.DailySummary, error)
	GetDailySummary(ctx utils.Context, date string) (*model.DailySummary, error)
	GetReleaseSummary(ctx utils.Context, releaseTriggerId model.ID, triagePhase *model.TriagePhase) (*respM.GetReleaseSummaryResponse, error)
}

type ResourceCohortService interface {
	List(ctx utils.Context, resourcepool string) ([]*model.ResourceCohort, error)
	Get(ctx utils.Context, name string) (*model.ResourceCohort, error)
	Create(ctx utils.Context, cohort *reqM.ResourceCohortCreateRequest) error
	Delete(ctx utils.Context, name string) error
	// Add reourcepool to the resource cohort.
	AddResourcePool(ctx utils.Context, name, resourcepool string) error
	// Remove reourcepool from the resource cohort.
	RemoveResourcePool(ctx utils.Context, name, resourcepool string) error
}

type ResourcePoolService interface {
	Create(ctx utils.Context, rp *model.ResourcePool) (*model.ResourcePool, error)
	UpdateStatus(ctx utils.Context, rps []*model.ResourcePool) ([]*model.ResourcePool, error)
	Update(ctx utils.Context, rp *model.ResourcePool) error
	Delete(ctx utils.Context, RmsAlia string, Names []string) error
	List(ctx utils.Context, opts common.ResourcePoolQueryOpts) ([]*model.ResourcePool, int, error)
}

type RMSService interface {
	Create(ctx utils.Context, rms model.RMS) (*model.RMS, error)
	// Delete(ctx utils.Context, name string) error
	Update(ctx utils.Context, rms model.RMS) error
	List(ctx utils.Context) ([]*model.RMS, error)
	Get(ctx utils.Context, name string) (*model.RMS, error)
}

type ResourceDurationService interface {
	Get(ctx utils.Context, planExecID model.ID) ([]model.ResourceDuration, error)
}

type TestbedSetupService interface {
	List(ctx utils.Context, opts taskruns.ListTestbedSetupOpts) (*taskruns.TestbedSetupList, error)
	Get(ctx utils.Context, id model.ID, opts taskruns.GetTestbedSetupOpts) (*taskruns.TestbedSetup, error)
	Create(ctx utils.Context, run taskruns.TestbedSetup, opts taskruns.CreateTestbedSetupOpts) (*taskruns.TestbedSetup, error)
	UpdateSchedule(ctx utils.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error)
	UpdateRun(ctx utils.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error)
	UpdateReport(ctx utils.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error)
}

type SvcTaskService interface {
	List(ctx context.Context, opts taskruns.ListSvcTaskOpts) (*taskruns.SvcTaskList, error)
	Get(ctx context.Context, id model.ID, opts taskruns.GetSvcTaskOpts) (*taskruns.SvcTask, error)
	GetByName(ctx context.Context, name string, opts taskruns.GetSvcTaskOpts) (*taskruns.SvcTask, error)
	Create(ctx context.Context, obj taskruns.SvcTask, opts taskruns.CreateSvcTaskOpts) (*taskruns.SvcTask, error)
	Delete(ctx context.Context, id model.ID, opts taskruns.DeleteSvcTaskOpts) (*taskruns.SvcTask, error)
	Update(ctx context.Context, obj taskruns.SvcTask, opts taskruns.UpdateSvcTaskOpts) (*taskruns.SvcTask, error)
}

type SvcTaskRunService interface {
	List(ctx context.Context, opts taskruns.ListSvcTaskRunOpts) (*taskruns.SvcTaskRunList, error)
	Get(ctx context.Context, id model.ID, opts taskruns.GetSvcTaskRunOpts) (*taskruns.SvcTaskRun, error)
	Create(ctx context.Context, obj taskruns.SvcTaskRun, opts taskruns.CreateSvcTaskRunOpts) (*taskruns.SvcTaskRun, error)
	Sync(ctx context.Context, id model.ID) (*taskruns.SvcTaskRun, error)
	Delete(ctx context.Context, id model.ID, opts taskruns.DeleteSvcTaskRunOpts) (*taskruns.SvcTaskRun, error)
	UpdateStatus(ctx context.Context, obj taskruns.SvcTaskRun, opts taskruns.UpdateSvcTaskRunOpts) (*taskruns.SvcTaskRun, error)
}

type ReportService interface {
	GetPlanReport(ctx context.Context, id model.ID) (*model.TestReport, error)
}
