package openapi

import (
	"encoding/json"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	"github.com/swaggest/openapi-go/openapi3"
	"gopkg.in/yaml.v3"
)

// Generator OpenAPI 文档生成器
type Generator struct {
	Reflector *openapi3.Reflector
}

// SecuritySchemeAgentOpenAPI 表示代理开放接口 Header 签名认证方案
const SecuritySchemeAgentOpenAPI = "agent_open_api"

const (
	agentOpenAPIAccountSecurityName   = "AgentOpenAPIAccount"
	agentOpenAPIPasswordSecurityName  = "AgentOpenAPIPassword"
	agentOpenAPITimestampSecurityName = "AgentOpenAPITimestamp"
	agentOpenAPINonceSecurityName     = "AgentOpenAPINonce"
	agentOpenAPISignSecurityName      = "AgentOpenAPISign"
)

// NewGenerator 创建一个新的生成器实例
func NewGenerator(title, version string) *Generator {
	reflector := openapi3.Reflector{}
	reflector.Spec = &openapi3.Spec{
		Openapi: "3.0.3",
		Info: openapi3.Info{
			Title:   title,
			Version: version,
		},
	}

	g := &Generator{Reflector: &reflector}
	g.addBearerAuth()
	g.addAgentOpenAPIAuth()
	return g
}

// addBearerAuth 添加 Bearer Token 认证定义
func (g *Generator) addBearerAuth() {
	bearerFormat := "JWT"
	g.Reflector.Spec.ComponentsEns().SecuritySchemesEns().WithMapOfSecuritySchemeOrRefValuesItem(
		"BearerAuth",
		openapi3.SecuritySchemeOrRef{
			SecurityScheme: &openapi3.SecurityScheme{
				HTTPSecurityScheme: &openapi3.HTTPSecurityScheme{
					Scheme:       "bearer",
					BearerFormat: &bearerFormat,
				},
			},
		},
	)
	g.addErrorResponseSchema()
}

// addAgentOpenAPIAuth 添加代理开放接口 Header 签名认证定义
func (g *Generator) addAgentOpenAPIAuth() {
	g.addAPIKeyHeaderAuth(agentOpenAPIAccountSecurityName, "X-Agent-Account", "代理账号标识，填写代理登录平台使用的用户名或手机号，例如 agent001。")
	g.addAPIKeyHeaderAuth(agentOpenAPIPasswordSecurityName, "X-Agent-Password", "代理账号当前登录密码，同时作为 HMAC-SHA256 签名密钥；密码变更后必须使用新密码签名。")
	g.addAPIKeyHeaderAuth(agentOpenAPITimestampSecurityName, "X-Agent-Timestamp", "调用方生成的请求发起时间，支持 Unix 秒、Unix 毫秒或 RFC3339 时间，必须与签名原文 timestamp 行一致。")
	g.addAPIKeyHeaderAuth(agentOpenAPINonceSecurityName, "X-Agent-Nonce", "调用方每次请求自行生成的随机串，不需要平台提前分配；同一账号在 5 分钟内不可重复。")
	g.addAPIKeyHeaderAuth(agentOpenAPISignSecurityName, "X-Agent-Sign", "调用方按签名规则实时计算的 HMAC-SHA256 小写十六进制签名，不是固定值。")
}

// addAPIKeyHeaderAuth 添加一个 Header 类型的 API Key 认证定义
func (g *Generator) addAPIKeyHeaderAuth(name, header, description string) {
	g.Reflector.Spec.ComponentsEns().SecuritySchemesEns().WithMapOfSecuritySchemeOrRefValuesItem(
		name,
		openapi3.SecuritySchemeOrRef{
			SecurityScheme: &openapi3.SecurityScheme{
				APIKeySecurityScheme: &openapi3.APIKeySecurityScheme{
					Name:        header,
					In:          openapi3.APIKeySecuritySchemeInHeader,
					Description: &description,
				},
			},
		},
	)
}

// addErrorResponseSchema 添加错误响应 Schema 定义
func (g *Generator) addErrorResponseSchema() {
	objectType := openapi3.SchemaType("object")
	integerType := openapi3.SchemaType("integer")
	stringType := openapi3.SchemaType("string")
	dateTimeFormat := "date-time"

	var errorExample interface{} = "参数验证失败"
	var codeExample interface{} = 1001

	errorSchema := openapi3.SchemaOrRef{
		Schema: &openapi3.Schema{
			Type: &objectType,
			Properties: map[string]openapi3.SchemaOrRef{
				"code": {
					Schema: &openapi3.Schema{
						Type:        &integerType,
						Description: ptrString("错误码"),
						Example:     &codeExample,
					},
				},
				"msg": {
					Schema: &openapi3.Schema{
						Type:        &stringType,
						Description: ptrString("错误消息"),
						Example:     &errorExample,
					},
				},
				"data": {
					Schema: &openapi3.Schema{
						Type:        &objectType,
						Description: ptrString("错误详情（可选）"),
					},
				},
				"timestamp": {
					Schema: &openapi3.Schema{
						Type:        &stringType,
						Format:      &dateTimeFormat,
						Description: ptrString("时间戳"),
					},
				},
			},
			Required: []string{"code", "msg", "timestamp"},
		},
	}

	g.Reflector.Spec.ComponentsEns().SchemasEns().WithMapOfSchemaOrRefValuesItem("ErrorResponse", errorSchema)
}

func ptrString(s string) *string {
	return &s
}

// FileUploadField 定义文件上传字段
type FileUploadField struct {
	Name        string
	Description string
	Required    bool
}

// AddOperation 向 OpenAPI 规范中添加一个操作
// 参数:
//   - method: HTTP 方法（GET, POST, PUT, DELETE 等）
//   - path: API 路径
//   - summary: 操作摘要
//   - description: 详细说明，支持 Markdown 语法（可为空）
//   - input: 请求参数结构体（可为 nil）
//   - output: 响应结构体（可为 nil）
//   - tags: 标签列表
//   - requiresAuth: 是否需要认证
func (g *Generator) AddOperation(method, path, summary, description string, input interface{}, body interface{}, output interface{}, requiresAuth bool, tags ...string) {
	g.AddOperationWithSecurity(method, path, summary, description, input, body, output, requiresAuth, "", tags...)
}

// AddOperationWithSecurity 添加操作并支持指定 OpenAPI 认证方案
func (g *Generator) AddOperationWithSecurity(method, path, summary, description string, input interface{}, body interface{}, output interface{}, requiresAuth bool, securityScheme string, tags ...string) {
	op := openapi3.Operation{
		Summary: &summary,
		Tags:    tags,
	}

	if description != "" {
		op.Description = &description
	}

	// 反射输入 (请求参数/Body)
	if input != nil {
		// SetRequest 根据结构体标签自动检测 Body、Query 或 Path 参数
		if err := g.Reflector.SetRequest(&op, input, method); err != nil {
			panic(err) // 生成过程中出错直接 panic，以便快速发现问题
		}
	}

	// 显式传入的 JSON body，用于 DELETE 等方法不自动生成 requestBody 的场景
	if body != nil {
		tempOp := openapi3.Operation{}
		if err := g.Reflector.SetRequest(&tempOp, body, "POST"); err != nil {
			panic(err)
		}
		if tempOp.RequestBody != nil {
			op.RequestBody = tempOp.RequestBody
		}
	}

	// 反射输出 (响应 Body)
	if output != nil {
		// 将输出包裹在 envelope 中
		g.setEnvelopeResponse(&op, output, 200)
	}

	// 添加认证要求
	if requiresAuth {
		g.addSecurityRequirement(&op, securityScheme)
	}

	// 添加标准错误响应
	g.addStandardErrorResponses(&op, requiresAuth)

	// 将操作添加到规范中
	if err := g.Reflector.Spec.AddOperation(method, path, op); err != nil {
		panic(err)
	}
}

// AddMultipartOperation 添加支持文件上传的 multipart/form-data 操作
func (g *Generator) AddMultipartOperation(method, path, summary, description string, input interface{}, output interface{}, requiresAuth bool, fileFields []FileUploadField, tags ...string) {
	op := openapi3.Operation{
		Summary: &summary,
		Tags:    tags,
	}

	if description != "" {
		op.Description = &description
	}

	objectType := openapi3.SchemaType("object")
	stringType := openapi3.SchemaType("string")
	integerType := openapi3.SchemaType("integer")
	binaryFormat := "binary"

	properties := make(map[string]openapi3.SchemaOrRef)
	var requiredFields []string

	for _, f := range fileFields {
		properties[f.Name] = openapi3.SchemaOrRef{
			Schema: &openapi3.Schema{
				Type:        &stringType,
				Format:      &binaryFormat,
				Description: ptrString(f.Description),
			},
		}
		if f.Required {
			requiredFields = append(requiredFields, f.Name)
		}
	}

	if input != nil {
		formFields := parseFormFields(input)
		for _, field := range formFields {
			var schemaType *openapi3.SchemaType
			switch field.Type {
			case "integer":
				schemaType = &integerType
			default:
				schemaType = &stringType
			}
			schema := &openapi3.Schema{
				Type:        schemaType,
				Description: ptrString(field.Description),
			}
			if field.Min != nil {
				schema.Minimum = field.Min
			}
			if field.MaxLength != nil {
				schema.MaxLength = field.MaxLength
			}
			properties[field.Name] = openapi3.SchemaOrRef{Schema: schema}
			if field.Required {
				requiredFields = append(requiredFields, field.Name)
			}
		}
	}

	op.RequestBody = &openapi3.RequestBodyOrRef{
		RequestBody: &openapi3.RequestBody{
			Required: ptrBool(true),
			Content: map[string]openapi3.MediaType{
				"multipart/form-data": {
					Schema: &openapi3.SchemaOrRef{
						Schema: &openapi3.Schema{
							Type:       &objectType,
							Properties: properties,
							Required:   requiredFields,
						},
					},
				},
			},
		},
	}

	if output != nil {
		// 将输出包裹在 envelope 中
		g.setEnvelopeResponse(&op, output, 200)
	}

	if requiresAuth {
		g.addSecurityRequirement(&op, "")
	}

	g.addStandardErrorResponses(&op, requiresAuth)

	if err := g.Reflector.Spec.AddOperation(method, path, op); err != nil {
		panic(err)
	}
}

func ptrBool(b bool) *bool {
	return &b
}

type formFieldInfo struct {
	Name        string
	Type        string
	Description string
	Required    bool
	Min         *float64
	MaxLength   *int64
}

func parseFormFields(input interface{}) []formFieldInfo {
	var fields []formFieldInfo
	t := reflect.TypeOf(input)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return fields
	}

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)

		formTag := field.Tag.Get("form")
		if formTag == "" || formTag == "-" {
			continue
		}

		info := formFieldInfo{
			Name:        formTag,
			Description: field.Tag.Get("description"),
		}

		switch field.Type.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			info.Type = "integer"
		default:
			info.Type = "string"
		}

		validateTag := field.Tag.Get("validate")
		if strings.Contains(validateTag, "required") {
			info.Required = true
		}

		if minStr := field.Tag.Get("minimum"); minStr != "" {
			if min, err := strconv.ParseFloat(minStr, 64); err == nil {
				info.Min = &min
			}
		}

		if maxLenStr := field.Tag.Get("maxLength"); maxLenStr != "" {
			if maxLen, err := strconv.ParseInt(maxLenStr, 10, 64); err == nil {
				info.MaxLength = &maxLen
			}
		}

		fields = append(fields, info)
	}

	return fields
}

// setEnvelopeResponse 设置包裹在 envelope 中的响应
func (g *Generator) setEnvelopeResponse(op *openapi3.Operation, output interface{}, statusCode int) {
	// 首先调用 SetJSONResponse 让 Reflector 处理 DTO schema
	tempOp := openapi3.Operation{}
	if err := g.Reflector.SetJSONResponse(&tempOp, output, statusCode); err != nil {
		panic(err)
	}

	// 获取生成的 DTO schema
	dtoSchemaOrRef := tempOp.Responses.MapOfResponseOrRefValues[strconv.Itoa(statusCode)].Response.Content["application/json"].Schema

	objectType := openapi3.SchemaType("object")
	integerType := openapi3.SchemaType("integer")
	stringType := openapi3.SchemaType("string")
	dateTimeFormat := "date-time"

	var successCodeExample interface{} = 0
	var successMsgExample interface{} = "success"

	// 构造 envelope schema
	envelopeSchema := &openapi3.SchemaOrRef{
		Schema: &openapi3.Schema{
			Type: &objectType,
			Properties: map[string]openapi3.SchemaOrRef{
				"code": {
					Schema: &openapi3.Schema{
						Type:        &integerType,
						Description: ptrString("响应码"),
						Example:     &successCodeExample,
					},
				},
				"msg": {
					Schema: &openapi3.Schema{
						Type:        &stringType,
						Description: ptrString("响应消息"),
						Example:     &successMsgExample,
					},
				},
				"data": *dtoSchemaOrRef,
				"timestamp": {
					Schema: &openapi3.Schema{
						Type:        &stringType,
						Format:      &dateTimeFormat,
						Description: ptrString("时间戳"),
					},
				},
			},
			Required: []string{"code", "msg", "data", "timestamp"},
		},
	}

	// 设置响应
	statusStr := strconv.Itoa(statusCode)
	description := "成功"
	if op.Responses.MapOfResponseOrRefValues == nil {
		op.Responses.MapOfResponseOrRefValues = make(map[string]openapi3.ResponseOrRef)
	}
	op.Responses.MapOfResponseOrRefValues[statusStr] = openapi3.ResponseOrRef{
		Response: &openapi3.Response{
			Description: description,
			Content: map[string]openapi3.MediaType{
				"application/json": {
					Schema: envelopeSchema,
				},
			},
		},
	}
}

// addSecurityRequirement 为操作添加认证要求
func (g *Generator) addSecurityRequirement(op *openapi3.Operation, securityScheme string) {
	if securityScheme == SecuritySchemeAgentOpenAPI {
		op.Security = []map[string][]string{
			{
				agentOpenAPIAccountSecurityName:   {},
				agentOpenAPIPasswordSecurityName:  {},
				agentOpenAPITimestampSecurityName: {},
				agentOpenAPINonceSecurityName:     {},
				agentOpenAPISignSecurityName:      {},
			},
		}
		return
	}

	op.Security = []map[string][]string{
		{"BearerAuth": {}},
	}
}

// addStandardErrorResponses 添加标准错误响应
func (g *Generator) addStandardErrorResponses(op *openapi3.Operation, requiresAuth bool) {
	if op.Responses.MapOfResponseOrRefValues == nil {
		op.Responses.MapOfResponseOrRefValues = make(map[string]openapi3.ResponseOrRef)
	}

	// 400 Bad Request - 所有端点都可能返回
	desc400 := "请求参数错误"
	op.Responses.MapOfResponseOrRefValues["400"] = openapi3.ResponseOrRef{
		Response: &openapi3.Response{
			Description: desc400,
			Content: map[string]openapi3.MediaType{
				"application/json": {
					Schema: &openapi3.SchemaOrRef{
						SchemaReference: &openapi3.SchemaReference{
							Ref: "#/components/schemas/ErrorResponse",
						},
					},
				},
			},
		},
	}

	// 401 Unauthorized - 仅认证端点返回
	if requiresAuth {
		desc401 := "未认证或认证已过期"
		op.Responses.MapOfResponseOrRefValues["401"] = openapi3.ResponseOrRef{
			Response: &openapi3.Response{
				Description: desc401,
				Content: map[string]openapi3.MediaType{
					"application/json": {
						Schema: &openapi3.SchemaOrRef{
							SchemaReference: &openapi3.SchemaReference{
								Ref: "#/components/schemas/ErrorResponse",
							},
						},
					},
				},
			},
		}

		// 403 Forbidden - 仅认证端点返回
		desc403 := "无权访问"
		op.Responses.MapOfResponseOrRefValues["403"] = openapi3.ResponseOrRef{
			Response: &openapi3.Response{
				Description: desc403,
				Content: map[string]openapi3.MediaType{
					"application/json": {
						Schema: &openapi3.SchemaOrRef{
							SchemaReference: &openapi3.SchemaReference{
								Ref: "#/components/schemas/ErrorResponse",
							},
						},
					},
				},
			},
		}
	}

	// 500 Internal Server Error - 所有端点都可能返回
	desc500 := "服务器内部错误"
	op.Responses.MapOfResponseOrRefValues["500"] = openapi3.ResponseOrRef{
		Response: &openapi3.Response{
			Description: desc500,
			Content: map[string]openapi3.MediaType{
				"application/json": {
					Schema: &openapi3.SchemaOrRef{
						SchemaReference: &openapi3.SchemaReference{
							Ref: "#/components/schemas/ErrorResponse",
						},
					},
				},
			},
		},
	}
}

// Save 将规范导出为 YAML 文件
func (g *Generator) Save(filename string) error {
	// 确保目录存在
	dir := filepath.Dir(filename)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	// 安全的方法：MarshalJSON -> Unmarshal -> MarshalYAML
	// 这确保了我们遵守 openapi3 库中定义的 `json` 标签
	jsonBytes, err := g.Reflector.Spec.MarshalJSON()
	if err != nil {
		return err
	}

	var obj interface{}
	if err := json.Unmarshal(jsonBytes, &obj); err != nil {
		return err
	}

	yamlBytes, err := yaml.Marshal(obj)
	if err != nil {
		return err
	}

	return os.WriteFile(filename, yamlBytes, 0644)
}
