package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"

	"github.com/creasty/defaults"
	echo "github.com/labstack/echo/v4"
	"github.com/tidbcloud/pov-service/internal/common/errors"
	"github.com/tidbcloud/pov-service/internal/common/params"
	"github.com/tidbcloud/pov-service/internal/config"
	"github.com/tidbcloud/pov-service/internal/handler/dto"
	"github.com/tidbcloud/pov-service/internal/handler/mymiddleware"
	"github.com/tidbcloud/pov-service/internal/model"
	"github.com/tidbcloud/pov-service/internal/pkg/icontext"
	"github.com/tidbcloud/pov-service/internal/pkg/log"
	"github.com/tidbcloud/pov-service/internal/service"
	"github.com/tidbcloud/pov-service/pkg/browser"
	"go.uber.org/dig"
)

type Workload struct {
	service.Workload
	Script service.Script
}

type WorkloadParams struct {
	dig.In
	*config.Config
	log.Logger

	service.Workload
	service.Script
}

type WorkloadResult struct {
	Result
}

func NewWorkload(
	params WorkloadParams,
) (Result, error) {
	handler := &Workload{
		Workload: params.Workload,
		Script:   params.Script,
	}
	return Result{
		Handler: handler,
	}, nil
}

func (w *Workload) Register(e *echo.Echo) error {
	// Workload Template related API
	v1tmpl := e.Group("/api/v1/workloads")
	{
		v1tmpl.GET("", w.ListWorkloadTemplates)
		v1tmpl.GET("/:id", w.GetWorkloadTemplate)
		v1tmpl.GET("/:id/sample/db", w.GetPreservedSampleWorkloadDB)
		v1tmpl.GET("/:id/sample/data", w.GetPreservedSampleWorkloadData)
	}

	// Workload Test related API
	v1test := e.Group("/api/v1/workloads")
	{
		v1test.GET("/tests/:id", w.GetWorkloadTest)
		v1test.GET("/tests", w.ListWorkloadTests)
		v1test.GET("/tests/:id/sample/db", w.SampleWorkloadTestDB)
		v1test.GET("/tests/:id/sample/data", w.SampleWorkloadTestData)
		v1test.GET("/tests/:id/audit", w.ListAuditLog)

		// Apply JWT middleware only to the RunWorkloadTest endpoint
		v1test.POST("/tests", w.RunWorkloadTest, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.POST("/tests/:id/stop", w.StopWorkloadTest, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))

		v1test.POST("/tests/:id/cluster", w.TriggerCreateCluster, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.POST("/tests/:id/prepare", w.TriggerPrepareData, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.POST("/tests/:id/traffic", w.TriggerRunTraffic, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))

		v1test.POST("/tests/:id/reports/pdf", w.DownloadTestReport, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))

		v1test.POST("/tests/:id/cluster/scale", w.ScaleCluster, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.PATCH("/tests/:id", w.UpdateWorkloadTest, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.PATCH("/tests/:id/cluster/ru", w.UpdateClusterRU, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.POST("/tests/:id/failure", w.InjectFailure, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
		v1test.PATCH("/tests/:id/conn", w.UpdateWorkloadTestMaxConn, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))

		// most of script management apis are only used internally
		// TODO: limit access to internal apis
		v1tmpl.GET("/scripts/templates", w.ListWorkloadScriptTemplates)
		v1tmpl.POST("/scripts/templates", w.CreateWorkloadScriptTemplate)
		v1tmpl.PUT("/scripts/templates/:id", w.UpdateWorkloadScriptTemplate)
		v1tmpl.DELETE("/scripts/templates/:id", w.DeleteWorkloadScriptTemplate)
		v1tmpl.GET("/scripts", w.ListWorkloadScripts)
		v1tmpl.POST("/scripts", w.CreateWorkloadScript)
		v1tmpl.PUT("/scripts/:id", w.UpdateWorkloadScript)
		v1tmpl.DELETE("/scripts/:id", w.DeleteWorkloadScript)
		v1tmpl.POST("/tests/:id/scripts/:scriptId", w.ActivateWorkloadScript, mymiddleware.JWTWithConfig(mymiddleware.DefaultJWTConfig()))
	}

	return nil
}

// ListWorkloadTemplates lists all available workload(templates)
// @Summary List Available Workloads
// @Description Lists all available workload (templates).
// @Tags workload
// @Accept json
// @Produce json
// @Param type query string false "Workload type" Enums(HTAP,OLTP,OLAP)
// @Param keyword query string false "Keyword to search in workload name or description"
// @Success 200 {object} dto.ListWorkloadResp "List of workloads"
// @Failure 400 {object} object "invalid config"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads [get]
// @ID ListWorkloadTemplates
func (w *Workload) ListWorkloadTemplates(c echo.Context) error {
	req := dto.ListWorkloadReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)

	if err := req.Validate(); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	params := params.ListWorkloads{
		Type:     req.Type,
		Keyword:  req.Keyword,
		Industry: req.Industry,

		Sort:    req.Sort,
		OrderBy: req.OrderBy,
		// PageSize: req.PageSize,
	}

	// Start GetWorkloadUsage in a goroutine
	wlCntChan := make(chan map[int64]int, 1)
	errChan := make(chan error, 1)

	go func() {
		wlCnt, err := w.GetWorkloadUsage(c.Request().Context())
		if err != nil {
			log.Log.Errorf(err, "GetWorkloadUsage failed")
			errChan <- err
			return
		}
		wlCntChan <- wlCnt
	}()

	// Start ListWorkloads in main goroutine
	wls, err := w.ListWorkloads(c.Request().Context(), params)
	if err != nil {
		log.Log.Errorf(err, "ListWorkloads failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	var wlCnt map[int64]int
	select {
	case wlCnt = <-wlCntChan:
	case err := <-errChan:
		// Error from GetWorkloadUsage, use empty map and log error
		wlCnt = make(map[int64]int)
		log.Log.Errorf(err, "GetWorkloadUsage failed, proceeding with empty count")
	}

	return c.JSON(http.StatusOK, dto.ListWorkloadResp{
		Workloads: dto.NewWorkloadResps(wls, wlCnt),
		PageResp: dto.PageResp{
			TotalSize: len(wls),
		},
	})
}

// GetWorkloadTemplate retrieves a workload template by ID
// @Summary Get Workload Template
// @Description Retrieves a workload template by the given ID, including usage statistics.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload ID"
// @Success 200 {object} dto.WorkloadResp "Workload template retrieved successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 404 {object} object "Workload not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/{id} [get]
// @ID GetWorkloadTemplate
func (w *Workload) GetWorkloadTemplate(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	intId, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	// Start GetWorkloadUsage in a goroutine
	wlCntChan := make(chan map[int64]int, 1)
	errChan := make(chan error, 1)

	go func() {
		wlCnt, err := w.GetWorkloadUsage(context.Background())
		if err != nil {
			log.Log.Errorf(err, "GetWorkloadUsage failed")
			errChan <- err
			return
		}
		wlCntChan <- wlCnt
	}()

	wl, err := w.GetWorkload(c.Request().Context(), intId)
	if err != nil {
		log.Log.Errorf(err, "ListWorkloads failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	var wlCnt map[int64]int
	select {
	case wlCnt = <-wlCntChan:
	case err := <-errChan:
		// Error from GetWorkloadUsage, use empty map and log error
		wlCnt = make(map[int64]int)
		log.Log.Errorf(err, "GetWorkloadUsage failed, proceeding with empty count")
	}

	cnt := wlCnt[wl.ID]

	return c.JSON(http.StatusOK, dto.WorkloadResp{
		Workload:  wl,
		TestCount: cnt,
	})
}

// RunWorkloadTest executes a new workload test
// @Summary Start Workload Test
// @Description Runs a workload test with the provided configuration, this test will automatically process the cluster
// creation, data preparation and traffic generation.
// @Tags workload
// @Accept json
// @Produce json
// @Param Authorization header string true "Bearer JWT token"
// @Param request body dto.RunWorkloadReq true "Workload test configuration"
// @Success 201 {object} dto.WorkloadTestResp "Workload test executed successfully"
// @Failure 400 {object} object "invalid config"
// @Failure 401 {object} object "unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "workload not found"
// @Failure 500 {object} object "internal server error"
// @Router /api/v1/workloads/tests [post]
// @ID RunWorkloadTest
func (w *Workload) RunWorkloadTest(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}

	// Log the user performing the action (optional)
	log.Log.Infof("User %s is starting a workload test", userEmail)
	p := dto.RunWorkloadReq{}
	if err := c.Bind(&p); err != nil {
		return err
	}
	defaults.MustSet(&p)
	if err := p.Validate(); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("invalid config: %w", err))
	}

	wlTest, err := w.Workload.RunWorkloadTest(
		c.Request().Context(), p.RunWorkload, userEmail)
	if err != nil {
		log.Log.Errorf(err, "RunWorkloadTest failed")
		if err, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusCreated, dto.WorkloadTestResp{
		WorkloadTest: wlTest,
	})
}

// GetWorkloadTest retrieves a workload test by ID
// @Summary Get A Specific Test
// @Description Retrieves a workload test result by the ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Success 200 {object} dto.WorkloadTestResp "Workload test result"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id} [get]
// @ID GetWorkloadTest
func (w *Workload) GetWorkloadTest(c echo.Context) error {
	id := c.Param("id")

	wl, err := w.Workload.GetWorkloadTest(c.Request().Context(), id)
	if err != nil {
		log.Log.Errorf(err, "GetWorkloadTest failed")
		if err, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.WorkloadTestResp{WorkloadTest: wl})
}

// ListWorkloadTests lists all workload tests
// @Summary List Workload Tests
// @Description Lists all workload tests with optional filtering and sorting.
// @Tags workload
// @Accept json
// @Produce json
// @Param sort query string false "Sort order for workload tests (e.g., createdAt)"
// @Param orderBy query string false "Order by direction (asc or desc)" Enums(asc,desc)
// @Param status query string false "Filter by workload test status" Enums(Created,Running,Succeeded,Failed,Canceled)
// @Success 200 {object} dto.ListWorkloadTestResp "List of workload tests"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests [get]
// @ID ListWorkloadTests
func (w *Workload) ListWorkloadTests(c echo.Context) error {
	req := dto.ListWorkloadTestsReq{
		ListReq: dto.ListReq{
			Sort:    params.SortDesc,
			OrderBy: "workload_tests.created_at",
		},
	}

	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	params := params.ListWorkloadTests{
		Sort:    req.Sort,
		OrderBy: req.OrderBy,
		Status:  req.Status,

		PageSize: req.PageSize,
		Page:     req.Page,
	}
	wls, err := w.Workload.ListWorkloadTests(
		c.Request().Context(), params)
	if err != nil {
		log.Log.Errorf(err, "ListWorkloadTests failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK,
		dto.ListWorkloadTestResp{
			Tests: dto.NewWorkloadTestResps(wls),
			PageResp: dto.PageResp{
				TotalSize: len(wls),
				Page:      req.Page,
				PageSize:  req.PageSize,
			},
		})
}

// StopWorkloadTest stops a running workload test by ID
// @Summary Stop Workload Test
// @Description Stops a running workload test by the ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Success 200 {object} dto.WorkloadTestResp "Workload test stopped successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 401 {object} object "unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/stop [post]
// @ID StopWorkloadTest
func (w *Workload) StopWorkloadTest(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")

	wl, err := w.Workload.StopWorkloadTest(ctx, id)
	if err != nil {
		log.Log.Errorf(err, "StopWorkloadTest failed")
		if err, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.WorkloadTestResp{WorkloadTest: wl})
}

// ScaleCluster scales a cluster by ID
// @Summary Scale Cluster
// @Description Scales a cluster by the ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.ScaleClusterReq true "Scale cluster request"
// @Success 200 "Cluster scaled successfully"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 401 {object} object "Unauthorized"
// @Failure 500 {object} object "Internal server error"
// @Security Bearer
// @Router /api/v1/workloads/tests/{id}/cluster/scale [post]
// @ID ScaleCluster
func (w *Workload) ScaleCluster(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")

	req := dto.ScaleClusterReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	if req.Replicas <= 0 {
		return echo.NewHTTPError(http.StatusBadRequest, "replicas must be greater than 0")
	}

	err := w.Workload.ScaleCluster(ctx, id, req.Replicas)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusOK)
}

// UpdateClusterRU updates the RU (Request Unit) settings for a cluster by workload test ID
// @Summary Update Cluster RU
// @Description Updates the minimum and maximum RU settings for a cluster by the workload test ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.UpdateClusterRUReq true "Update cluster RU request"
// @Success 200 "Cluster RU updated successfully"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 401 {object} object "Unauthorized"
// @Failure 500 {object} object "Internal server error"
// @Security Bearer
// @Router /api/v1/workloads/tests/{id}/cluster/ru [patch]
// @ID UpdateClusterRU
func (w *Workload) UpdateClusterRU(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")

	req := dto.UpdateClusterRUReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	if err := req.Validate(); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	err := w.Workload.UpdateClusterRU(ctx, id, req.MinRU, req.MaxRU)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusOK)
}

// InjectFailure injects a failure into a cluster for a workload test by ID
// @Summary Inject Failure
// @Description Injects a failure into a cluster for a workload test by the ID with specified duration.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.InjectFailureReq true "Inject failure request"
// @Success 204 "Failure injected successfully"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 401 {object} object "Unauthorized"
// @Failure 500 {object} object "Internal server error"
// @Security Bearer
// @Router /api/v1/workloads/tests/{id}/failure [post]
// @ID InjectFailure
func (w *Workload) InjectFailure(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")

	req := dto.InjectFailureReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	if err := req.Validate(); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	err := w.Workload.InjectFailure(ctx, id, req.Duration)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusNoContent)
}

// UpdateWorkloadTestMaxConn updates the maximum connections for a workload test by ID
// @Summary Update Workload Test Max Connections
// @Description Updates the maximum connections for a workload test by the ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.UpdateWorkloadTestMaxConnReq true "Update max connections request"
// @Success 200 "Workload test max connections updated successfully"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 401 {object} object "Unauthorized"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/conn [patch]
// @Security Bearer
// @ID UpdateWorkloadTestMaxConn
func (w *Workload) UpdateWorkloadTestMaxConn(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")

	req := dto.UpdateWorkloadTestMaxConnReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	if err := req.Validate(); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	err := w.UpdateTrafficMaxConn(ctx, id, req.MaxConn)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusOK)
}

// UpdateWorkloadTest updates the workload test configuration
// @Summary Update Workload Test
// @Description Updates the configuration of a workload test by the ID, including max connections and RU settings.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.UpdateWorkloadTestReq true "Workload test configuration update request"
// @Success 200 "Workload test updated successfully"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 401 {object} object "Unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id} [patch]
// @Security Bearer
// @ID UpdateWorkloadTest
func (w *Workload) UpdateWorkloadTest(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")

	req := dto.UpdateWorkloadTestReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	if err := req.Validate(); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	err := w.Workload.UpdateWorkloadTest(
		ctx, id,
		params.UpdateWorkloadTest{
			MaxConn: req.MaxConn,
			MinRU:   req.MinRU,
			MaxRU:   req.MaxRU,
		},
	)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusOK)
}

// @Summary Sample Workload Test DB
// @Description Samples the database of a workload test by ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.SampleDBReq true "Sample DB request"
// @Success 200 {object} dto.SampleDBResp "Database sample result"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/sample/db [get]
// @ID SampleWorkloadTestDB
func (w *Workload) SampleWorkloadTestDB(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	req := dto.SampleDBReq{}

	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	dbs, err := w.SampleDBs(c.Request().Context(), id, req.SampleDBOptions)
	if err != nil {
		if _, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}

		log.Log.Errorf(err, "SampleWorkloadTestDB failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK,
		dto.SampleDBResp{
			DBSampleResult: dbs,
		})
}

// @Summary Sample Workload Test Data
// @Description Samples the data of a workload test by ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Param request body dto.SampleDataReq true "Sample data request"
// @Success 200 {object} dto.SampleDataResp "Data sample result"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/sample/data [get]
// @ID SampleWorkloadTestData
func (w *Workload) SampleWorkloadTestData(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	req := dto.SampleDataReq{}

	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)
	data, err := w.SampleData(c.Request().Context(), id, req.SampleDataOptions)
	if err != nil {
		if _, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}

		log.Log.Errorf(err, "SampleWorkloadTestData failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK,
		dto.SampleDataResp{
			TableQueryResult: data,
		})
}

// ListAuditLog lists audit logs for a workload test
// @Summary List Audit Logs
// @Description Lists all audit logs for a workload test by ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Success 200 {object} dto.ListAuditLogResp "Audit logs retrieved successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/audit [get]
// @ID ListAuditLog
func (w *Workload) ListAuditLog(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	logs, err := w.Workload.ListAuditLog(c.Request().Context(), id)
	if err != nil {
		if _, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}

		log.Log.Errorf(err, "ListAuditLog failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK,
		dto.ListAuditLogResp{
			AuditLogs: logs,
			PageResp: dto.PageResp{
				TotalSize: len(logs),
			},
		})
}

// TriggerCreateCluster triggers cluster creation for a workload test
// @Summary Trigger Cluster Creation
// @Description Triggers cluster creation for a workload test by ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Success 200 {object} dto.WorkloadTestResp "Cluster creation triggered successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 401 {object} object "unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/cluster [post]
// @ID TriggerCreateCluster
func (w *Workload) TriggerCreateCluster(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	test, err := w.Workload.TriggerCreateCluster(ctx, id)
	if err != nil {
		if err, ok := err.(errors.InvalidArgument); ok {
			return echo.NewHTTPError(http.StatusBadRequest, err)
		}

		if err, ok := err.(errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.WorkloadTestResp{
		WorkloadTest: test,
	})
}

// TriggerPrepareData triggers data preparation for a workload test
// @Summary Trigger Data Preparation
// @Description Triggers data preparation for a workload test by ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Success 200 {object} dto.WorkloadTestResp "Data preparation triggered successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 401 {object} object "unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/prepare [post]
// @ID TriggerPrepareData
func (w *Workload) TriggerPrepareData(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	test, err := w.Workload.TriggerPrepareData(ctx, id)
	if err != nil {
		if err, ok := err.(errors.InvalidArgument); ok {
			return echo.NewHTTPError(http.StatusBadRequest, err)
		}

		if err, ok := err.(errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.WorkloadTestResp{
		WorkloadTest: test,
	})
}

// TriggerRunTraffic triggers traffic generation for a workload test
// @Summary Trigger Traffic Generation
// @Description Triggers traffic generation for a workload test by ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload Test ID"
// @Success 200 {object} dto.WorkloadTestResp "Traffic generation triggered successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 401 {object} object "unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/traffic [post]
// @ID TriggerRunTraffic
func (w *Workload) TriggerRunTraffic(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	test, err := w.Workload.TriggerRunTraffic(ctx, id)
	if err != nil {
		if err, ok := err.(errors.InvalidArgument); ok {
			return echo.NewHTTPError(http.StatusBadRequest, err)
		}
		if err, ok := err.(errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.WorkloadTestResp{
		WorkloadTest: test,
	})
}

// GetPreservedSampleWorkloadDB retrieves the preserved sample databases for a workload
// @Summary Get Preserved Sample Databases
// @Description Retrieves the preserved sample databases for a workload by workload ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload ID"
// @Success 200 {object} dto.SampleDBResp "Sample databases retrieved successfully"
// @Failure 400 {object} object "Invalid ID parameter"
// @Failure 404 {object} object "Workload not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/{id}/sample/db [get]
// @ID GetPreservedSampleWorkloadDB
func (w *Workload) GetPreservedSampleWorkloadDB(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	int64id, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	dbs, err := w.GetPreservedSampleDBs(c.Request().Context(), int64id)
	if err != nil {
		if _, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}

		log.Log.Errorf(err, "GetPreservedSampleWorkloadDB failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK,
		dto.SampleDBResp{
			DBSampleResult: dbs,
		})
}

// GetPreservedSampleWorkloadData retrieves the preserved sample data for a workload
// @Summary Get Preserved Sample Data
// @Description Retrieves the preserved sample data for a workload by workload ID.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Workload ID"
// @Param request body dto.SampleDataReq true "Sample data request"
// @Success 200 {object} dto.SampleDataResp "Data sample result"
// @Failure 400 {object} object "Invalid query parameters"
// @Failure 404 {object} object "Workload not found"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/{id}/sample/data [get]
// @ID GetPreservedSampleWorkloadData
func (w *Workload) GetPreservedSampleWorkloadData(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	int64id, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	req := dto.SampleDataReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&req)

	data, err := w.GetPreservedSampleData(
		c.Request().Context(), int64id, req.DBName, req.TableName)
	if err != nil {
		if _, ok := err.(*errors.NotFound); ok {
			return echo.NewHTTPError(http.StatusNotFound, err)
		}

		log.Log.Errorf(err, "GetPreservedSampleWorkloadData failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK,
		dto.SampleDataResp{
			TableQueryResult: data,
		})
}

// DownloadTestReport generates and downloads a PDF report for a workload test
// @Summary Download Test Report
// @Description Generates and downloads a PDF report for a workload test by ID. The report includes test configuration, performance metrics, and visualizations.
// @Tags workload
// @Accept json
// @Produce application/pdf
// @Param id path string true "Workload Test ID"
// @Param request body dto.DownloadTestReportReq true "PDF generation options"
// @Success 200 "PDF report generated successfully"
// @Failure 400 {object} object "Invalid ID parameter or request body"
// @Failure 401 {object} object "unauthorized - missing or invalid JWT token"
// @Failure 404 {object} object "Workload test not found"
// @Failure 500 {object} object "Internal server error or PDF generation failed"
// @Router /api/v1/workloads/tests/{id}/reports/pdf [post]
// @ID DownloadTestReport
func (w *Workload) DownloadTestReport(c echo.Context) error {
	// Get user email from JWT context (available due to JWT middleware)
	userEmail, ok := mymiddleware.GetUserEmailFromContext(c)
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "user email not found in JWT token")
	}
	ctx := icontext.NewUserEmailContext(c.Request().Context(), userEmail)

	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	p := dto.DownloadTestReportReq{}
	if err := c.Bind(&p); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	defaults.MustSet(&p)

	localStorageMap := make(map[string]string)
	err := json.Unmarshal([]byte(p.LocalStoragesJSON), &localStorageMap)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	localStorages := make([]browser.LocalStorage, 0)
	for k, v := range localStorageMap {
		localStorages = append(localStorages, browser.LocalStorage{
			Key:   k,
			Value: v,
		})
	}
	ctx = browser.NewLocalStorageContext(ctx, localStorages)

	file, err := w.DownloadTestPDF(
		ctx,
		id,
		browser.PrintPDFOptions{
			Address:       p.GetAddress(id),
			Width:         p.Width,
			Height:        p.Height,
			ReadySelector: p.GetReadySelector(),
		},
	)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to generate PDF: %v", err))
	}
	defer func() {
		_ = file.Close()
		_ = os.Remove(file.Name())
	}()

	return c.Attachment(file.Name(), file.Name())
}

// @Summary List Workload Script Templates
// @Description Lists all available workload script templates with their phases, including message, startDuration, and mutation details.
// @Tags workload
// @Accept json
// @Produce json
// @Success 200 {object} dto.ListScriptTemplatesResp "List of script templates"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/scripts/templates [get]
// @ID ListWorkloadScriptTemplates
func (w *Workload) ListWorkloadScriptTemplates(c echo.Context) error {
	tmpls, err := w.Script.ListTemplates(c.Request().Context())
	if err != nil {
		log.Log.Errorf(err, "ListWorkloadScriptTemplates failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	ret := dto.NewScriptTemplateResps(tmpls)

	return c.JSON(http.StatusOK, dto.ListScriptTemplatesResp{
		Templates: ret,
		PageResp: dto.PageResp{
			TotalSize: len(ret),
		},
	})

}

// We don't expose this api in swagger
func (w *Workload) CreateWorkloadScriptTemplate(c echo.Context) error {
	req := dto.ScriptTemplateUpsertReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	tmpl, err := req.ToModel()
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	created, err := w.Script.CreateTemplate(c.Request().Context(), tmpl)
	if err != nil {
		log.Log.Errorf(err, "CreateWorkloadScriptTemplate failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusCreated, dto.ScriptTemplateResp{ScriptTemplate: created})
}

// We don't expose this api in swagger
func (w *Workload) UpdateWorkloadScriptTemplate(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	intID, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	req := dto.ScriptTemplateUpsertReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	tmpl, err := req.ToModel()
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	tmpl.ID = intID

	updated, err := w.Script.UpdateTemplate(c.Request().Context(), tmpl)
	if err != nil {
		log.Log.Errorf(err, "UpdateWorkloadScriptTemplate failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.ScriptTemplateResp{ScriptTemplate: updated})
}

// We don't expose this api in swagger
func (w *Workload) DeleteWorkloadScriptTemplate(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	intID, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	if err := w.Script.DeleteTemplate(c.Request().Context(), intID); err != nil {
		log.Log.Errorf(err, "DeleteWorkloadScriptTemplate failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusNoContent)
}

// We don't expose this api in swagger
func (w *Workload) CreateWorkloadScript(c echo.Context) error {
	req := dto.ScriptUpsertReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	script, err := req.ToModel()
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	created, err := w.Script.Create(c.Request().Context(), script)
	if err != nil {
		log.Log.Errorf(err, "CreateWorkloadScript failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusCreated, dto.ScriptResp{Script: created})
}

// We don't expose this api in swagger
func (w *Workload) UpdateWorkloadScript(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	intID, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	if intID <= 0 {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid id")
	}

	req := dto.ScriptUpsertReq{}
	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	script, err := req.ToModel()
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	script.ID = intID

	updated, err := w.Script.Update(c.Request().Context(), script)
	if err != nil {
		log.Log.Errorf(err, "UpdateWorkloadScript failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, dto.ScriptResp{Script: updated})
}

// We don't expose this api in swagger
func (w *Workload) DeleteWorkloadScript(c echo.Context) error {
	id := c.Param("id")
	if id == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}

	intID, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	if intID <= 0 {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid id")
	}

	if err := w.Script.Delete(c.Request().Context(), intID); err != nil {
		log.Log.Errorf(err, "DeleteWorkloadScript failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.NoContent(http.StatusNoContent)
}

// @Summary Activate Workload Script
// @Description Activates a script for a specific workload test.
// @Tags workload
// @Accept json
// @Produce json
// @Param id path string true "Test ID"
// @Param scriptId path string true "Script ID"
// @Success 200 {object} object "Script activated successfully"
// @Failure 400 {object} object "Invalid parameters"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/tests/{id}/scripts/{scriptId} [post]
// @ID ActivateWorkloadScript
func (w *Workload) ActivateWorkloadScript(c echo.Context) error {
	testId := c.Param("id")
	if testId == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "id is required")
	}
	scriptId := c.Param("scriptId")
	if scriptId == "" {
		return echo.NewHTTPError(http.StatusBadRequest, "script id is required")
	}
	intScriptID, err := strconv.ParseInt(scriptId, 10, 64)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid script id")
	}
	if intScriptID <= 0 {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid script id")
	}

	ctx := context.Background()
	ctx = icontext.NewUserEmailContext(ctx, "tidbcloud-pov@pingcap.com")
	exec, err := w.Script.Activate(ctx, testId, intScriptID)
	if err != nil {
		log.Log.Errorf(err, "ActivateWorkloadScript failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	return c.JSON(http.StatusOK, exec)
}

// @Summary List Workload Script
// @Description Lists all available workload script with their phases.
// @Tags workload
// @Accept json
// @Produce json
// @Param sort query string false "Sort order for workload tests (e.g., createdAt)"
// @Param orderBy query string false "Order by direction (asc or desc)" Enums(asc,desc)
// @Param testID query string false "Filter by workload test ID"
// @Success 200 {object} dto.ListScriptResp "List of scripts"
// @Failure 500 {object} object "Internal server error"
// @Router /api/v1/workloads/scripts [get]
// @ID ListWorkloadScripts
func (w *Workload) ListWorkloadScripts(c echo.Context) error {
	req := dto.ListScriptReq{}

	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	defaults.MustSet(&req)
	listParams := params.ListScripts{
		TestID: req.TestID,
	}

	scripts, err := w.Script.List(c.Request().Context(), listParams)
	if err != nil {
		log.Log.Errorf(err, "ListWorkloadScript failed")
		return echo.NewHTTPError(http.StatusInternalServerError, err)
	}

	workloads := make(map[int64]model.Workload)
	if list, err := w.ListWorkloads(c.Request().Context(), params.ListWorkloads{}); err == nil {
		for _, wl := range list {
			workloads[wl.ID] = wl
		}
	}

	ret := dto.NewScriptResps(scripts)
	for i := range ret {
		if workload, ok := workloads[ret[i].WorkloadID]; ok {
			ret[i].WorkloadName = workload.Name
		}
	}

	return c.JSON(http.StatusOK, dto.ListScriptResp{
		Templates: ret,
		PageResp: dto.PageResp{
			TotalSize: len(ret),
		},
	})

}
