package dao

import (
	"context"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"github.com/tidbcloud/aitestsuit/internal/entity"
	"github.com/tidbcloud/aitestsuit/types"
)

var db *gorm.DB

func InitDB(d *gorm.DB) {
	db = d
}

func GetAllTestPlansByPage(ctx context.Context, limit, offset int) (int64, []entity.TestPlan, error) {
	var plans []entity.TestPlan
	var total int64

	err := db.Model(&entity.TestPlan{}).Count(&total).Error
	if err != nil {
		return total, nil, err
	}

	err = db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&plans).Error

	return total, plans, err
}

func GetTestPlanByID(ctx context.Context, id uint) (entity.TestPlan, error) {
	var plan entity.TestPlan

	err := db.Where("id = ?", id).First(&plan).Error

	return plan, err
}

func GetAIModelByID(ctx context.Context, id uint) (entity.AIModel, error) {
	var aiModel entity.AIModel

	err := db.Where("id = ?", id).First(&aiModel).Error

	return aiModel, err
}

func GetTestRunByID(ctx context.Context, id uint) (entity.TestRun, error) {
	var run entity.TestRun

	err := db.Where("id = ?", id).First(&run).Error

	return run, err
}

func GetTestCasesByPlanIDAndPage(ctx context.Context, planID, limit, offset int) (int64, []entity.TestCase, error) {
	var cases []entity.TestCase
	var total int64

	q := db.Model(&entity.TestCase{}).Where("plan_id = ?", planID)

	err := q.Count(&total).Error
	if err != nil {
		return total, nil, err
	}

	err = q.Order("created_at DESC").Limit(limit).Offset(offset).Find(&cases).Error

	return total, cases, err
}

func GetAllTestCasesByPlanID(ctx context.Context, planID uint) ([]entity.TestCase, error) {
	var cases []entity.TestCase

	err := db.Where("plan_id = ?", planID).Order("created_at DESC").Find(&cases).Error

	return cases, err
}

func GetTestCaseByID(ctx context.Context, id uint) (entity.TestCase, error) {
	var testCase entity.TestCase

	err := db.Where("id = ?", id).First(&testCase).Error

	return testCase, err
}

func DeleteTestCaseByID(ctx context.Context, id uint) error {
	return db.Unscoped().Delete(&entity.TestCase{}, id).Error
}

func UpdateTestCase(ctx context.Context, testCase *entity.TestCase) error {
	return db.Save(&testCase).Error
}

func GetPromptTemplates(ctx context.Context, limit, offset int) ([]entity.PromptTemplate, error) {
	var prompts []entity.PromptTemplate

	err := db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&prompts).Error

	return prompts, err
}

func GetPromptTemplateByID(ctx context.Context, id uint) (entity.PromptTemplate, error) {
	var template entity.PromptTemplate

	err := db.Where("id = ?", id).First(&template).Error

	return template, err
}

func GetTestReports(ctx context.Context, filter types.GetReportsFilterSchema, limit, offset int) (int64, []entity.TestReport, error) {
	var reports []entity.TestReport
	var total int64

	q := db.Model(&entity.TestReport{})

	if filter.PlanID > 0 {
		q = q.Where("plan_id = ?", filter.PlanID)
	}
	if filter.RunID > 0 {
		q = q.Where("test_run_id = ?", filter.RunID)
	}

	err := q.Count(&total).Error
	if err != nil {
		return total, nil, err
	}

	err = q.Order("created_at DESC").Limit(limit).Offset(offset).Find(&reports).Error

	return total, reports, err
}

func GetTestReport(ctx context.Context, id uint) (entity.TestReport, error) {
	var report entity.TestReport

	err := db.Where("id = ?", id).First(&report).Error

	return report, err
}

func GetTestRuns(ctx context.Context, limit, offset int) ([]entity.TestRun, error) {
	var runs []entity.TestRun

	err := db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&runs).Error

	return runs, err
}

func GetTestRun(ctx context.Context, id uint) (entity.TestRun, error) {
	var run entity.TestRun

	err := db.Where("id = ?", id).First(&run).Error

	return run, err
}

func GetTestResultsByReportIDAndPage(ctx context.Context, reportID uint, filter types.GetReportDetailFilterSchema, limit, offset int) (int64, []entity.TestCaseResult, error) {
	var results []entity.TestCaseResult
	var total int64

	q := db.Model(&entity.TestCaseResult{}).Where("test_report_id = ?", reportID)

	if filter.ResultAStatus >= 0 {
		q = q.Where("result_a = ?", filter.ResultAStatus)
	}

	err := q.Count(&total).Error

	if err != nil {
		return 0, nil, err
	}

	err = q.Order("created_at DESC").Limit(limit).Offset(offset).Find(&results).Error

	return total, results, err
}

func CreateTestPlan(ctx context.Context, plan *entity.TestPlan) error {
	return db.Create(plan).Error
}

func CreateTestCase(ctx context.Context, testCase *entity.TestCase) error {
	return db.Create(testCase).Error
}

func GetTestReportByRunID(ctx context.Context, runID uint, limit int) ([]entity.TestReport, error) {
	var reports []entity.TestReport

	err := db.Where("test_run_id = ?", runID).Order("pass_rate DESC").Limit(limit).Find(&reports).Error

	return reports, err
}

func MustCountCasesByPlanID(ctx context.Context, planID uint) int64 {
	var count int64

	err := db.Model(&entity.TestCase{}).Where("plan_id = ?", planID).Count(&count).Error
	if err != nil {
		tilog.Panic(ctx, "failed to count cases by plan id", zap.Error(err))
	}

	return count
}

func MustCountRunsByPlanID(ctx context.Context, planID uint) int64 {
	var count int64

	err := db.Model(&entity.TestRun{}).Where("plan_id = ?", planID).Count(&count).Error
	if err != nil {
		tilog.Panic(ctx, "failed to count cases by plan id", zap.Error(err))
	}

	return count
}

func MustCountReportsByPlanID(ctx context.Context, planID uint) int64 {
	var count int64

	err := db.Model(&entity.TestReport{}).Where("plan_id = ?", planID).Count(&count).Error
	if err != nil {
		tilog.Panic(ctx, "failed to count cases by plan id", zap.Error(err))
	}

	return count
}

func GetLastTestReportByPlanID(ctx context.Context, planID uint) (entity.TestReport, error) {
	var report entity.TestReport

	err := db.Where("plan_id = ?", planID).Order("created_at DESC").First(&report).Error

	return report, err
}

func GetAllTestCaseResultsByCaseID(ctx context.Context, caseID uint) ([]entity.TestCaseResult, error) {
	var items []entity.TestCaseResult

	err := db.Where("test_case_id = ?", caseID).Find(&items).Error

	return items, err
}

func GetLastTestReportByRunIDAndPlanIDAndStatus(ctx context.Context, runID, planID uint, status int) (entity.TestReport, error) {
	var report entity.TestReport

	err := db.Where("test_run_id = ? AND plan_id = ? AND status = ?", runID, planID, status).Order("created_at DESC").First(&report).Error

	return report, err
}

func GetTestPlanByName(ctx context.Context, planName string) (entity.TestPlan, error) {
	var plan entity.TestPlan

	err := db.Where("name = ?", planName).First(&plan).Error

	return plan, err
}

func CreateTestRun(ctx context.Context, testRun *entity.TestRun) error {
	return db.Create(testRun).Error
}

func CreatePromptTemplate(ctx context.Context, template *entity.PromptTemplate) error {
	return db.Create(template).Error
}

func UpdatePromptTemplate(ctx context.Context, template *entity.PromptTemplate) error {
	return db.Save(template).Error
}

func CreateTestReport(ctx context.Context, report *entity.TestReport) error {
	return db.Create(report).Error
}

func CreateTestCaseResult(ctx context.Context, result *entity.TestCaseResult) error {
	return db.Create(result).Error
}

func UpdateTestCaseResult(ctx context.Context, result *entity.TestCaseResult) error {
	return db.Save(result).Error
}

func UpdateTestReport(ctx context.Context, report *entity.TestReport) error {
	return db.Save(report).Error
}

func CreateAIModel(ctx context.Context, aiModel *entity.AIModel) error {
	return db.Create(&aiModel).Error
}

func UpdateAIModel(ctx context.Context, aiModel *entity.AIModel) error {
	return db.Save(&aiModel).Error
}

func GetAIModels(ctx context.Context, limit, offset int) ([]entity.AIModel, error) {
	var items []entity.AIModel

	err := db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&items).Error

	return items, err
}

func GetAllReportsByPlanID(ctx context.Context, planID uint) ([]entity.TestReport, error) {
	var reports []entity.TestReport

	err := db.Model(&entity.TestReport{}).Where("plan_id = ?", planID).Find(&reports).Error
	if err != nil {
		return reports, err
	}

	return reports, nil
}
