//go:generate ${GOBIN}/mockgen -destination github_action_client_mock.go -package=githubcli ${GO_MODULE}/pkg/infra/clients/githubcli Client
package githubcli

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/go-resty/resty/v2"
)

type DeployWorkflowReq struct {
	Ref    string `json:"ref"`
	Inputs Inputs `json:"inputs"`
}

type Inputs struct {
	TiDBCloudInstance string `json:"tidb-cloud-instance"`
	RegionName        string `json:"region-name"`
	AWSRegion         string `json:"aws-region"`
	AdvanceOptions    string `json:"advance-options"`
}

// WorkflowRun represents a GitHub Actions workflow run.
type WorkflowRun struct {
	ID         int64  `json:"id"`
	Status     string `json:"status"`     // "queued", "in_progress", "completed"
	Conclusion string `json:"conclusion"` // "success", "failure", "cancelled", etc.
	HTMLURL    string `json:"html_url"`
}

// dispatchResponse is the response from workflow_dispatch (HTTP 200).
type dispatchResponse struct {
	WorkflowRunID int64  `json:"workflow_run_id"`
	RunURL        string `json:"run_url"`
	HTMLURL       string `json:"html_url"`
}

type Client interface {
	// DeployWorkflow dispatches a workflow and returns the workflow run ID and URL.
	DeployWorkflow(ctx context.Context, workflowID string, req interface{}) (int64, string, error)
	GetWorkflowRun(ctx context.Context, runID int64) (*WorkflowRun, error)
	ListWorkflowRuns(ctx context.Context, workflowID string, branch string, limit int) ([]*WorkflowRun, error)
}

type client struct {
	httpCli *resty.Client
	owner   string
	repo    string
	token   string
}

func NewClient(owner string, repo string, token string) (Client, error) {
	httpCli := resty.New().
		SetTransport(http.DefaultTransport).
		SetTimeout(10 * time.Second).
		SetBaseURL("https://api.github.com").
		OnAfterResponse(func(c *resty.Client, r *resty.Response) error {
			if !r.IsSuccess() {
				return fmt.Errorf("call github service failed, url: %s, status: %s, body: %s", r.Request.URL, r.Status(), string(r.Body()))
			}
			return nil
		})

	return &client{httpCli: httpCli, owner: owner, repo: repo, token: token}, nil
}

func (c *client) DeployWorkflow(ctx context.Context, workflowID string, req interface{}) (int64, string, error) {
	var resp dispatchResponse
	_, err := c.httpCli.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"owner":       c.owner,
			"repo":        c.repo,
			"workflow_id": workflowID,
		}).
		SetHeaders(map[string]string{
			"Accept":               "application/vnd.github+json",
			"X-GitHub-Api-Version": "2026-03-10",
		}).
		SetAuthToken(c.token).
		SetBody(req).
		SetResult(&resp).
		Post("/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches")
	if err != nil {
		return 0, "", err
	}
	runURL := resp.HTMLURL
	if runURL == "" && resp.WorkflowRunID > 0 {
		runURL = fmt.Sprintf("https://github.com/%s/%s/actions/runs/%d", c.owner, c.repo, resp.WorkflowRunID)
	}
	return resp.WorkflowRunID, runURL, nil
}

func (c *client) GetWorkflowRun(ctx context.Context, runID int64) (*WorkflowRun, error) {
	var run WorkflowRun
	_, err := c.httpCli.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"owner":  c.owner,
			"repo":   c.repo,
			"run_id": fmt.Sprintf("%d", runID),
		}).
		SetHeaders(map[string]string{
			"Accept":               "application/vnd.github+json",
			"X-GitHub-Api-Version": "2022-11-28",
		}).
		SetAuthToken(c.token).
		SetResult(&run).
		Get("/repos/{owner}/{repo}/actions/runs/{run_id}")
	if err != nil {
		return nil, err
	}
	return &run, nil
}

type listRunsResponse struct {
	WorkflowRuns []*WorkflowRun `json:"workflow_runs"`
}

func (c *client) ListWorkflowRuns(ctx context.Context, workflowID string, branch string, limit int) ([]*WorkflowRun, error) {
	var resp listRunsResponse
	req := c.httpCli.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"owner":       c.owner,
			"repo":        c.repo,
			"workflow_id": workflowID,
		}).
		SetHeaders(map[string]string{
			"Accept":               "application/vnd.github+json",
			"X-GitHub-Api-Version": "2022-11-28",
		}).
		SetAuthToken(c.token).
		SetResult(&resp).
		SetQueryParam("per_page", fmt.Sprintf("%d", limit))
	if branch != "" {
		req = req.SetQueryParam("branch", branch)
	}
	_, err := req.Get("/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")
	if err != nil {
		return nil, err
	}
	return resp.WorkflowRuns, nil
}
