package framework

import (
	"encoding/json"
	"os"
	"time"
)

// CaseResult results of a test case
type CaseResult struct {
	CaseName    string        `json:"caseName"`
	Description string        `json:"description,omitempty"`
	Success     bool          `json:"success"`
	Errors      []string      `json:"errors,omitempty"`
	Duration    time.Duration `json:"duration"`
	DurationMs  int64         `json:"durationMs"`
	Request     *RequestInfo  `json:"request,omitempty"`
	Response    *ResponseInfo `json:"response,omitempty"`
}

// RequestInfo request information (for debugging)
type RequestInfo struct {
	Method  string            `json:"method"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers,omitempty"`
	Body    interface{}       `json:"body,omitempty"`
}

// ResponseInfo response information (for debugging)
type ResponseInfo struct {
	StatusCode int               `json:"statusCode"`
	Headers    map[string]string `json:"headers,omitempty"`
	Body       interface{}       `json:"body,omitempty"`
}

// NewCaseResult creates a new CaseResult instance
func NewCaseResult() *CaseResult {
	return &CaseResult{
		Success: true,
		Errors:  make([]string, 0),
	}
}

// AddError add an error message to the case result
func (cr *CaseResult) AddError(err string) *CaseResult {
	cr.Success = false
	cr.Errors = append(cr.Errors, err)
	return cr
}

// SetRequest set request information
func (cr *CaseResult) SetRequest(method, url string, body interface{}) *CaseResult {
	cr.Request = &RequestInfo{
		Method: method,
		URL:    url,
		Body:   body,
	}
	return cr
}

// SetRequestWithHeaders set request information with headers
func (cr *CaseResult) SetRequestWithHeaders(method, url string, headers map[string]string, body interface{}) *CaseResult {
	cr.Request = &RequestInfo{
		Method:  method,
		URL:     url,
		Headers: headers,
		Body:    body,
	}
	if v, ok := headers["Authorization"]; ok {
		headers["Authorization"] = v[:8] + "****"
	}
	return cr
}

// SetResponse set response information
func (cr *CaseResult) SetResponse(statusCode int, body interface{}) *CaseResult {
	cr.Response = &ResponseInfo{
		StatusCode: statusCode,
		Body:       body,
	}
	return cr
}

// SetResponseWithHeaders set response information with headers
func (cr *CaseResult) SetResponseWithHeaders(statusCode int, headers map[string]string, body interface{}) *CaseResult {
	cr.Response = &ResponseInfo{
		StatusCode: statusCode,
		Headers:    headers,
		Body:       body,
	}
	if v, ok := headers["Authorization"]; ok {
		headers["Authorization"] = v[:8] + "****"
	}
	return cr
}

// TestReport test report containing multiple case results
type TestReport struct {
	StartTime   time.Time     `json:"startTime"`
	EndTime     time.Time     `json:"endTime"`
	Duration    time.Duration `json:"duration"`
	DurationMs  int64         `json:"durationMs"`
	ClusterID   string        `json:"clusterId"`
	GSURL       string        `json:"gsUrl"`
	GatewayURL  string        `json:"gatewayUrl,omitempty"`
	ClinicURL   string        `json:"clinicUrl"`
	BizType     string        `json:"bizType"`
	RegionID    string        `json:"regionId"`
	TotalCases  int           `json:"totalCases"`
	PassedCases int           `json:"passedCases"`
	FailedCases int           `json:"failedCases"`
	Results     []*CaseResult `json:"results"`
}

func NewTestReport() *TestReport {
	return &TestReport{
		Results: make([]*CaseResult, 0),
	}
}

func (tr *TestReport) AddResult(result *CaseResult) {
	result.DurationMs = result.Duration.Milliseconds()
	tr.Results = append(tr.Results, result)
	if result.Success {
		tr.PassedCases++
	} else {
		tr.FailedCases++
	}
}

func (tr *TestReport) ToJSON() (string, error) {
	tr.DurationMs = tr.Duration.Milliseconds()
	data, err := json.MarshalIndent(tr, "", "  ")
	if err != nil {
		return "", err
	}
	return string(data), nil
}

func (tr *TestReport) SaveToFile(path string) error {
	jsonStr, err := tr.ToJSON()
	if err != nil {
		return err
	}
	return os.WriteFile(path, []byte(jsonStr), 0644)
}

func (tr *TestReport) IsAllPassed() bool {
	return tr.FailedCases == 0
}

func (tr *TestReport) GetFailedResults() []*CaseResult {
	failed := make([]*CaseResult, 0)
	for _, r := range tr.Results {
		if !r.Success {
			failed = append(failed, r)
		}
	}
	return failed
}
