package tushare

import (
	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	"github.com/tidbcloud/AgentTeam/internal/extension/spec"
)

// Action name constants for the Tushare catalog.
const (
	ActionListStocks           = "list-stocks"
	ActionGetTradeCalendar     = "get-trade-calendar"
	ActionGetDailyQuotes       = "get-daily-quotes"
	ActionGetDailyBasic        = "get-daily-basic"
	ActionGetAdjustmentFactors = "get-adjustment-factors"
	ActionGetShareholderTrades = "get-shareholder-trades"
)

// Definition returns the Agent-visible Tushare catalog.
func Definition() registry.Definition {
	return registry.Definition{
		Domain:      Domain,
		Version:     1,
		Name:        "Tushare",
		Description: "Query A-share market data through the official Tushare Pro HTTP interface: stock basics, trading calendar, daily quotes, daily valuation indicators, adjustment factors, and important shareholder trades.",
		Actions:     actions(),
	}
}

func readSafety() spec.Safety {
	return spec.Safety{Class: "read", Idempotent: true, Destructive: false}
}

func actions() []registry.DefinitionAction {
	return []registry.DefinitionAction{
		listStocksAction(),
		tradeCalendarAction(),
		datedAction(ActionGetDailyQuotes, "quotes", "Daily quote rows returned by Tushare daily.", dailyQuoteRowProperties(), "Get A-share daily quote rows through Tushare daily with normalized rows."),
		datedAction(ActionGetDailyBasic, "dailyBasics", "Daily basic indicator rows returned by Tushare.", dailyBasicRowProperties(), "Get A-share daily valuation and share indicators through Tushare daily_basic."),
		datedAction(ActionGetAdjustmentFactors, "adjustmentFactors", "Adjustment factor rows returned by Tushare.", adjustmentFactorRowProperties(), "Get A-share adjustment factor rows through Tushare adj_factor."),
		shareholderTradesAction(),
	}
}

func listStocksAction() registry.DefinitionAction {
	return registry.DefinitionAction{
		Name:        ActionListStocks,
		Summary:     "List A-share stock basics",
		Description: "List A-share stock basic information through Tushare stock_basic with normalized rows.",
		Safety:      readSafety(),
		InputSchema: objectSchema(map[string]any{
			"exchange":   nonEmptyStringSchema("Exchange code, such as SSE, SZSE, or BSE."),
			"listStatus": enumStringSchema("Listing status: L listed, D delisted, P paused.", "L", "D", "P"),
			"isHs":       enumStringSchema("Shanghai-Hong Kong or Shenzhen-Hong Kong Stock Connect flag.", "N", "H", "S"),
		}, nil),
		OutputSchema: mappedOutputSchema("stocks", "Stocks returned by Tushare stock_basic.", stockBasicRowProperties()),
	}
}

func tradeCalendarAction() registry.DefinitionAction {
	return registry.DefinitionAction{
		Name:        ActionGetTradeCalendar,
		Summary:     "Get the exchange trading calendar",
		Description: "Get exchange trading calendar rows through Tushare trade_cal with normalized dates.",
		Safety:      readSafety(),
		InputSchema: objectSchema(map[string]any{
			"exchange":  nonEmptyStringSchema("Exchange code, such as SSE or SZSE."),
			"startDate": yyyymmddSchema("Start date in YYYYMMDD format."),
			"endDate":   yyyymmddSchema("End date in YYYYMMDD format."),
			"isOpen": map[string]any{
				"type": "integer", "minimum": 0, "maximum": 1,
				"description": "Whether to return open days only, where 1 is open and 0 is closed.",
			},
		}, []string{"startDate", "endDate"}),
		OutputSchema: mappedOutputSchema("calendar", "Trade calendar rows returned by Tushare.", tradeCalendarRowProperties()),
	}
}

// datedAction builds one of the dated market-data actions sharing the same
// filter contract and envelope shape.
func datedAction(name, outputKey, outputDescription string, rowProperties map[string]any, description string) registry.DefinitionAction {
	return registry.DefinitionAction{
		Name:        name,
		Summary:     description,
		Description: description,
		Safety:      readSafety(),
		InputSchema: objectSchema(map[string]any{
			"tsCode":    nonEmptyStringSchema("Tushare security code, such as 000001.SZ."),
			"tradeDate": yyyymmddSchema("Trade date in YYYYMMDD format."),
			"startDate": yyyymmddSchema("Start date in YYYYMMDD format."),
			"endDate":   yyyymmddSchema("End date in YYYYMMDD format."),
		}, nil),
		OutputSchema: mappedOutputSchema(outputKey, outputDescription, rowProperties),
	}
}

func shareholderTradesAction() registry.DefinitionAction {
	return registry.DefinitionAction{
		Name:        ActionGetShareholderTrades,
		Summary:     "Get important shareholder trades",
		Description: "Get A-share important shareholder increases and decreases through Tushare stk_holdertrade.",
		Safety:      readSafety(),
		InputSchema: objectSchema(map[string]any{
			"tsCode":           nonEmptyStringSchema("Tushare security code, such as 000001.SZ."),
			"announcementDate": yyyymmddSchema("Announcement date in YYYYMMDD format."),
			"startDate":        yyyymmddSchema("Announcement start date in YYYYMMDD format."),
			"endDate":          yyyymmddSchema("Announcement end date in YYYYMMDD format."),
			"tradeType":        enumStringSchema("Share trade type: IN for an increase or DE for a decrease.", "IN", "DE"),
			"holderType":       enumStringSchema("Shareholder type: C for a company, P for an individual, or G for an executive.", "C", "P", "G"),
		}, nil),
		OutputSchema: mappedOutputSchema("shareholderTrades", "A-share important shareholder increase and decrease records.", shareholderTradeRowProperties()),
	}
}

func mappedOutputSchema(outputKey, outputDescription string, rowProperties map[string]any) map[string]any {
	return objectSchema(map[string]any{
		"requestId": stringSchema("Tushare request identifier returned by the API."),
		"message":   nullableStringSchema("Tushare response message, or null for successful responses."),
		outputKey:   arrayOfSchema(outputDescription, objectSchema(rowProperties, nil)),
	}, nil)
}

func stockBasicRowProperties() map[string]any {
	return map[string]any{
		"tsCode":     nullableStringSchema("Tushare security code."),
		"symbol":     nullableStringSchema("Exchange-local security symbol."),
		"name":       nullableStringSchema("Chinese security name."),
		"area":       nullableStringSchema("Company region."),
		"industry":   nullableStringSchema("Company industry."),
		"market":     nullableStringSchema("Market board name."),
		"exchange":   nullableStringSchema("Exchange code."),
		"listStatus": nullableStringSchema("Listing status."),
		"listDate":   nullableStringSchema("Listing date in YYYYMMDD format."),
		"delistDate": nullableStringSchema("Delisting date in YYYYMMDD format when present."),
		"isHs":       nullableStringSchema("Stock Connect flag when present."),
	}
}

func tradeCalendarRowProperties() map[string]any {
	return map[string]any{
		"exchange":     nullableStringSchema("Exchange code."),
		"calDate":      nullableStringSchema("Calendar date in YYYYMMDD format."),
		"isOpen":       nullableIntegerSchema("Whether the date is an open trading day, where 1 is open."),
		"pretradeDate": nullableStringSchema("Previous trading date in YYYYMMDD format."),
	}
}

func dailyQuoteRowProperties() map[string]any {
	return map[string]any{
		"tsCode":    nullableStringSchema("Tushare security code."),
		"tradeDate": nullableStringSchema("Trade date in YYYYMMDD format."),
		"open":      nullableNumberSchema("Open price."),
		"high":      nullableNumberSchema("High price."),
		"low":       nullableNumberSchema("Low price."),
		"close":     nullableNumberSchema("Close price."),
		"preClose":  nullableNumberSchema("Previous close price."),
		"change":    nullableNumberSchema("Price change."),
		"pctChg":    nullableNumberSchema("Percentage change."),
		"vol":       nullableNumberSchema("Trading volume in lots."),
		"amount":    nullableNumberSchema("Trading amount."),
	}
}

func dailyBasicRowProperties() map[string]any {
	return map[string]any{
		"tsCode":        nullableStringSchema("Tushare security code."),
		"tradeDate":     nullableStringSchema("Trade date in YYYYMMDD format."),
		"close":         nullableNumberSchema("Close price."),
		"turnoverRate":  nullableNumberSchema("Turnover rate."),
		"turnoverRateF": nullableNumberSchema("Free-float turnover rate."),
		"volumeRatio":   nullableNumberSchema("Volume ratio."),
		"pe":            nullableNumberSchema("Price-to-earnings ratio."),
		"peTtm":         nullableNumberSchema("Trailing price-to-earnings ratio."),
		"pb":            nullableNumberSchema("Price-to-book ratio."),
		"ps":            nullableNumberSchema("Price-to-sales ratio."),
		"psTtm":         nullableNumberSchema("Trailing price-to-sales ratio."),
		"dvRatio":       nullableNumberSchema("Dividend yield ratio."),
		"dvTtm":         nullableNumberSchema("Trailing dividend yield ratio."),
		"totalShare":    nullableNumberSchema("Total shares."),
		"floatShare":    nullableNumberSchema("Floating shares."),
		"freeShare":     nullableNumberSchema("Free-float shares."),
		"totalMv":       nullableNumberSchema("Total market value."),
		"circMv":        nullableNumberSchema("Circulating market value."),
	}
}

func adjustmentFactorRowProperties() map[string]any {
	return map[string]any{
		"tsCode":    nullableStringSchema("Tushare security code."),
		"tradeDate": nullableStringSchema("Trade date in YYYYMMDD format."),
		"adjFactor": nullableNumberSchema("Adjustment factor."),
	}
}

func shareholderTradeRowProperties() map[string]any {
	return map[string]any{
		"tsCode":            nullableStringSchema("Tushare security code."),
		"announcementDate":  nullableStringSchema("Announcement date in YYYYMMDD format."),
		"holderName":        nullableStringSchema("Shareholder name."),
		"holderType":        nullableStringSchema("Shareholder type: G executive, P individual, or C company."),
		"tradeType":         nullableStringSchema("Share trade type: IN increase or DE decrease."),
		"changeVolume":      nullableNumberSchema("Number of shares changed."),
		"changeRatio":       nullableNumberSchema("Change as a percentage of circulating shares."),
		"sharesAfterChange": nullableNumberSchema("Shares held after the change."),
		"ratioAfterChange":  nullableNumberSchema("Holding after the change as a percentage of circulating shares."),
		"averagePrice":      nullableNumberSchema("Average transaction price."),
		"totalShares":       nullableNumberSchema("Total shares held."),
		"beginDate":         nullableStringSchema("Shareholding change start date in YYYYMMDD format."),
		"closeDate":         nullableStringSchema("Shareholding change end date in YYYYMMDD format."),
	}
}
