package ones

// Input and output JSON Schema builders for the ONES catalog. Schemas are
// plain map literals; passthrough envelopes use loose objects because ONES
// responses carry deployment-specific field sets. Identifier and length
// constraints mirror the official Open API contract.

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

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

func noInputSchema() map[string]any {
	return objectSchema(map[string]any{}, nil)
}

func rawObjectSchema(description string) map[string]any {
	return map[string]any{
		"$schema":              schemaDialect,
		"type":                 "object",
		"description":          description,
		"additionalProperties": true,
	}
}

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

func rawArraySchema(key, description string) map[string]any {
	return map[string]any{
		"type":        "array",
		"items":       rawObjectSchema(description),
		"description": description,
	}
}

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

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

func nonEmptyStringSchema(description string) map[string]any {
	return boundedStringSchema(description, 2048)
}

// uuid8Schema accepts the official 8-character ONES uuids: team, user,
// issue type, task status, sprint, priority option, and wiki space/page
// identifiers.
func uuid8Schema(description string) map[string]any {
	return map[string]any{
		"type":        "string",
		"pattern":     "^[A-Za-z0-9]{8}$",
		"description": description,
	}
}

// uuid16Schema accepts the official 16-character ONES uuids: project, work
// item, and parent work item identifiers (creator uuid + 8 random
// characters).
func uuid16Schema(description string) map[string]any {
	return map[string]any{
		"type":        "string",
		"pattern":     "^[A-Za-z0-9]{16}$",
		"description": description,
	}
}

// nullableUUID16Schema is the nullable variant of uuid16Schema.
func nullableUUID16Schema(description string) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{"type": "null"},
			uuid16Schema(description),
		},
		"description": description,
	}
}

// nullableUUID8Schema is the nullable variant of uuid8Schema.
func nullableUUID8Schema(description string) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{"type": "null"},
			uuid8Schema(description),
		},
		"description": description,
	}
}

// uuid16OrNumberSchema accepts a 16-character work item uuid or a decimal
// serial number, the two route identifier shapes of the task detail
// endpoint.
func uuid16OrNumberSchema(description string) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{
				"type":    "string",
				"pattern": "^(?:[A-Za-z0-9]{16}|[0-9]{1,10})$",
			},
			map[string]any{"type": "integer", "minimum": 0, "maximum": 2147483647},
		},
		"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 integerSchema(description string) map[string]any {
	return map[string]any{"type": "integer", "description": description}
}

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

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

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

func stringArraySchema(description string, minItems int) map[string]any {
	schema := map[string]any{
		"type":        "array",
		"items":       map[string]any{"type": "string", "minLength": 1, "maxLength": 64},
		"description": description,
	}
	if minItems > 0 {
		schema["minItems"] = minItems
	}
	return schema
}

// uuid8ArraySchema is an array of 8-character user/option uuids.
func uuid8ArraySchema(description string, minItems int) map[string]any {
	schema := map[string]any{
		"type":        "array",
		"items":       uuid8Schema("One uuid entry."),
		"description": description,
	}
	if minItems > 0 {
		schema["minItems"] = minItems
	}
	return schema
}

// uuid16ArraySchema is an array of 16-character project/work item uuids.
func uuid16ArraySchema(description string, minItems int) map[string]any {
	schema := map[string]any{
		"type":        "array",
		"items":       uuid16Schema("One uuid entry."),
		"description": description,
	}
	if minItems > 0 {
		schema["minItems"] = minItems
	}
	return schema
}

// nullableBoundedStringSchema is a nullable string with a documented cap.
func nullableBoundedStringSchema(description string, maxLength int) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{"type": "null"},
			map[string]any{"type": "string", "maxLength": maxLength},
		},
		"description": description,
	}
}

// nullableIntegerArraySchema is a nullable array of integers.
func nullableIntegerArraySchema(description string) map[string]any {
	return map[string]any{
		"anyOf": []any{
			map[string]any{"type": "null"},
			map[string]any{"type": "array", "items": map[string]any{"type": "integer"}},
		},
		"description": description,
	}
}

// looseObjectArraySchema is an array of free-form objects (raw passthrough).
func looseObjectArraySchema(description string, minItems int) map[string]any {
	schema := map[string]any{
		"type":        "array",
		"items":       rawObjectSchema(description),
		"description": description,
	}
	if minItems > 0 {
		schema["minItems"] = minItems
	}
	return schema
}

// teamUUIDSchema is the shared team scoping parameter of every ONES action.
var teamUUIDSchema = uuid8Schema(
	"The ONES team uuid; discover it with get-auth-info (teams[].uuid).")
