package jin10

const schemaDialect = "https://json-schema.org/draft/2020-12/schema"

func objectSchema(
	description string,
	properties map[string]any,
	required ...string,
) map[string]any {
	schema := map[string]any{
		"$schema":              schemaDialect,
		"type":                 "object",
		"description":          description,
		"additionalProperties": false,
		"properties":           properties,
	}
	if len(required) > 0 {
		requiredValues := make([]any, 0, len(required))
		for _, name := range required {
			requiredValues = append(requiredValues, name)
		}
		schema["required"] = requiredValues
	}
	return schema
}

func stringSchema(description string) map[string]any {
	return map[string]any{
		"type":        "string",
		"description": description,
	}
}

func nonEmptyStringSchema(description string) map[string]any {
	return map[string]any{
		"type":        "string",
		"minLength":   1,
		"description": description,
	}
}

func nullableStringSchema(description string) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{"type": "null"},
			map[string]any{"type": "string"},
		},
		"description": description,
	}
}

func emptyInputSchema() map[string]any {
	schema := objectSchema("No input is required.", map[string]any{})
	return schema
}

func codeInputSchema() map[string]any {
	return objectSchema(
		"Select one Jin10 quote instrument.",
		map[string]any{
			"code": nonEmptyStringSchema(
				"The quote instrument code, such as XAUUSD."),
		},
		"code",
	)
}

func klineInputSchema() map[string]any {
	return objectSchema(
		"Select a Jin10 instrument and an optional recent K-line range.",
		map[string]any{
			"code": nonEmptyStringSchema(
				"The quote instrument code, such as XAUUSD."),
			"time": map[string]any{
				"type":        "integer",
				"description": "The start Unix timestamp in seconds, within the last 24 hours.",
			},
			"count": map[string]any{
				"type":        "integer",
				"minimum":     1,
				"maximum":     100,
				"description": "The number of minute K-lines to return.",
			},
		},
		"code",
	)
}

func cursorInputSchema() map[string]any {
	return objectSchema(
		"Read one cursor-paginated Jin10 page.",
		map[string]any{
			"cursor": stringSchema(
				"The next_cursor value returned by the previous page."),
		},
	)
}

func keywordInputSchema() map[string]any {
	return objectSchema(
		"Search Jin10 content by keyword.",
		map[string]any{
			"keyword": nonEmptyStringSchema("The search keyword."),
		},
		"keyword",
	)
}

func keywordCursorInputSchema() map[string]any {
	return objectSchema(
		"Search cursor-paginated Jin10 content by keyword.",
		map[string]any{
			"keyword": nonEmptyStringSchema("The search keyword."),
			"cursor": stringSchema(
				"The next_cursor value returned by the previous page."),
		},
		"keyword",
	)
}

func newsIDInputSchema() map[string]any {
	return objectSchema(
		"Select one Jin10 news article.",
		map[string]any{
			"id": nonEmptyStringSchema("The Jin10 article ID."),
		},
		"id",
	)
}

func statusProperties(data map[string]any) map[string]any {
	return map[string]any{
		"data": data,
		"status": map[string]any{
			"type":        "integer",
			"const":       200,
			"description": "The Jin10 status code; 200 means success.",
		},
		"message": stringSchema("The Jin10 status message."),
	}
}

func statusOutputSchema(description string, data map[string]any) map[string]any {
	return objectSchema(
		description,
		statusProperties(data),
		"data",
		"status",
		"message",
	)
}

func nullableSchema(value map[string]any, description string) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{"type": "null"},
			value,
		},
		"description": description,
	}
}

func quoteCodeSchema() map[string]any {
	return objectSchema(
		"A supported Jin10 quote instrument.",
		map[string]any{
			"code": stringSchema("The instrument code."),
			"name": stringSchema("The instrument display name."),
		},
		"code",
		"name",
	)
}

func quoteCodesOutputSchema() map[string]any {
	return statusOutputSchema(
		"The supported Jin10 quote instruments.",
		map[string]any{
			"type":        "array",
			"items":       quoteCodeSchema(),
			"description": "The supported instrument list.",
		},
	)
}

func quoteSchema() map[string]any {
	return objectSchema(
		"A real-time Jin10 quote.",
		map[string]any{
			"code":        stringSchema("The instrument code."),
			"name":        stringSchema("The instrument name."),
			"time":        stringSchema("The quote publication time."),
			"open":        stringSchema("The opening price."),
			"close":       stringSchema("The latest or closing price."),
			"high":        stringSchema("The highest price."),
			"low":         stringSchema("The lowest price."),
			"volume":      map[string]any{"type": "integer"},
			"ups_price":   stringSchema("The price change."),
			"ups_percent": stringSchema("The percentage price change."),
		},
		"code",
		"name",
		"time",
		"open",
		"close",
		"high",
		"low",
		"volume",
		"ups_price",
		"ups_percent",
	)
}

func quoteOutputSchema() map[string]any {
	return statusOutputSchema(
		"The Jin10 real-time quote response.",
		nullableSchema(quoteSchema(), "The quote, or null when unavailable."),
	)
}

func klineItemSchema() map[string]any {
	return objectSchema(
		"One minute-level Jin10 K-line.",
		map[string]any{
			"time":   map[string]any{"type": "integer"},
			"open":   stringSchema("The opening price."),
			"close":  stringSchema("The closing price."),
			"high":   stringSchema("The highest price."),
			"low":    stringSchema("The lowest price."),
			"volume": map[string]any{"type": "integer"},
		},
		"time",
		"open",
		"close",
		"high",
		"low",
		"volume",
	)
}

func klineOutputSchema() map[string]any {
	data := objectSchema(
		"Jin10 K-lines for one instrument.",
		map[string]any{
			"code": stringSchema("The instrument code."),
			"name": stringSchema("The instrument name."),
			"klines": map[string]any{
				"type":        "array",
				"items":       klineItemSchema(),
				"description": "Minute-level K-lines.",
			},
		},
		"code",
		"name",
		"klines",
	)
	return statusOutputSchema(
		"The Jin10 K-line response.",
		nullableSchema(data, "The K-line result, or null when unavailable."),
	)
}

func flashItemSchema() map[string]any {
	return objectSchema(
		"One Jin10 flash update.",
		map[string]any{
			"title":   stringSchema("The optional flash title."),
			"content": stringSchema("The flash body."),
			"time":    stringSchema("The publication time."),
			"url":     map[string]any{"type": "string", "format": "uri"},
		},
		"content",
		"time",
		"url",
	)
}

func newsSummarySchema() map[string]any {
	return objectSchema(
		"One Jin10 news article summary.",
		map[string]any{
			"id":           stringSchema("The article ID."),
			"title":        stringSchema("The article title."),
			"introduction": stringSchema("The article introduction."),
			"time":         stringSchema("The publication time."),
			"url":          map[string]any{"type": "string", "format": "uri"},
		},
		"id",
		"title",
		"introduction",
		"time",
		"url",
	)
}

func pageDataSchema(items map[string]any, description string) map[string]any {
	return objectSchema(
		description,
		map[string]any{
			"items": map[string]any{
				"type":        "array",
				"items":       items,
				"description": "The records on this page.",
			},
			"next_cursor": stringSchema(
				"The next page cursor, or an empty string at the end."),
			"has_more": map[string]any{
				"type":        "boolean",
				"description": "Whether another page is available.",
			},
		},
		"items",
		"next_cursor",
		"has_more",
	)
}

func flashPageOutputSchema() map[string]any {
	return statusOutputSchema(
		"The paginated Jin10 flash response.",
		nullableSchema(
			pageDataSchema(flashItemSchema(), "A page of Jin10 flash updates."),
			"The page, or null when unavailable.",
		),
	)
}

func flashSearchOutputSchema() map[string]any {
	data := objectSchema(
		"Jin10 flash search results.",
		map[string]any{
			"items": map[string]any{
				"type":        "array",
				"maxItems":    150,
				"items":       flashItemSchema(),
				"description": "The matching flash updates.",
			},
		},
		"items",
	)
	return statusOutputSchema(
		"The Jin10 flash search response.",
		nullableSchema(data, "The search result, or null when unavailable."),
	)
}

func newsPageOutputSchema() map[string]any {
	return statusOutputSchema(
		"The paginated Jin10 news response.",
		nullableSchema(
			pageDataSchema(newsSummarySchema(), "A page of Jin10 news summaries."),
			"The page, or null when unavailable.",
		),
	)
}

func newsDetailSchema() map[string]any {
	return objectSchema(
		"One complete Jin10 news article.",
		map[string]any{
			"id":           stringSchema("The article ID."),
			"title":        stringSchema("The article title."),
			"introduction": stringSchema("The article introduction."),
			"content":      stringSchema("The article body."),
			"time":         stringSchema("The publication time."),
			"url":          map[string]any{"type": "string", "format": "uri"},
		},
		"id",
		"title",
		"introduction",
		"content",
		"time",
		"url",
	)
}

func newsDetailOutputSchema() map[string]any {
	return statusOutputSchema(
		"The Jin10 article detail response.",
		nullableSchema(
			newsDetailSchema(),
			"The article, or null when unavailable.",
		),
	)
}

func calendarItemSchema() map[string]any {
	return objectSchema(
		"One Jin10 economic calendar event.",
		map[string]any{
			"pub_time":   stringSchema("The scheduled publication time."),
			"star":       map[string]any{"type": "integer"},
			"title":      stringSchema("The indicator title."),
			"previous":   nullableStringSchema("The previous value."),
			"consensus":  nullableStringSchema("The consensus forecast."),
			"actual":     nullableStringSchema("The actual published value."),
			"revised":    nullableStringSchema("The revised value."),
			"affect_txt": stringSchema("The market impact description."),
		},
		"pub_time",
		"star",
		"title",
		"previous",
		"consensus",
		"actual",
		"revised",
		"affect_txt",
	)
}

func calendarOutputSchema() map[string]any {
	return statusOutputSchema(
		"The current-week Jin10 economic calendar.",
		map[string]any{
			"type":        "array",
			"items":       calendarItemSchema(),
			"description": "The current natural week's economic events.",
		},
	)
}
