package jenkins

const (
	schemaDialect  = "https://json-schema.org/draft/2020-12/schema"
	maxSafeInteger = 9007199254740991
)

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

// looseJSONObjectSchema accepts any non-null JSON object while retaining the
// catalog-wide closed-root invariant. The catch-all pattern explicitly
// evaluates every property before additionalProperties:false is applied.
func looseJSONObjectSchema(description string) map[string]any {
	return map[string]any{
		"$schema":              schemaDialect,
		"type":                 "object",
		"additionalProperties": false,
		"description":          description,
		"patternProperties": map[string]any{
			".*": map[string]any{},
		},
	}
}

func jobPathSchema(allowRoot bool) map[string]any {
	schema := map[string]any{
		"type":        "array",
		"maxItems":    16,
		"description": "Jenkins folder and job names from the root to the target job.",
		"items": map[string]any{
			"type":        "string",
			"minLength":   1,
			"maxLength":   256,
			"description": "One folder or job name, limited to 256 UTF-8 bytes.",
			"pattern":     "^[^/\\\\\\x00-\\x1F\\x7F]+$",
			"not": map[string]any{
				"enum": []any{".", ".."},
			},
		},
	}
	if !allowRoot {
		schema["minItems"] = 1
	}
	return schema
}

func positiveSafeIntegerSchema(description string) map[string]any {
	return map[string]any{
		"type":        "integer",
		"minimum":     1,
		"maximum":     maxSafeInteger,
		"description": description,
	}
}

func nonNegativeSafeIntegerSchema(description string) map[string]any {
	return map[string]any{
		"type":        "integer",
		"minimum":     0,
		"maximum":     maxSafeInteger,
		"description": description,
	}
}

func listJobsInputSchema() map[string]any {
	return objectSchema(
		"Jenkins job-list input. Omit jobPath or use an empty array to list the root.",
		map[string]any{
			"jobPath": jobPathSchema(true),
		},
		nil,
	)
}

func jobInputSchema(description string) map[string]any {
	return objectSchema(
		description,
		map[string]any{
			"jobPath": jobPathSchema(false),
		},
		[]string{"jobPath"},
	)
}

func triggerBuildInputSchema() map[string]any {
	return objectSchema(
		"Jenkins build-trigger input.",
		map[string]any{
			"jobPath":    jobPathSchema(false),
			"parameters": buildParametersSchema(),
		},
		[]string{"jobPath"},
	)
}

func buildParametersSchema() map[string]any {
	return map[string]any{
		"type":          "object",
		"maxProperties": 100,
		"description":   "Optional scalar Jenkins build parameters keyed by parameter name.",
		"propertyNames": map[string]any{
			"type":        "string",
			"minLength":   1,
			"maxLength":   128,
			"description": "A parameter name limited to 128 UTF-8 bytes.",
			"pattern":     "^[^\\x00-\\x1F\\x7F]+$",
		},
		"additionalProperties": map[string]any{
			"anyOf": []any{
				map[string]any{
					"type":        "string",
					"maxLength":   16384,
					"description": "A control-free string limited to 16 KiB of UTF-8.",
					"pattern":     "^[^\\x00-\\x1F\\x7F]*$",
				},
				map[string]any{"type": "number"},
				map[string]any{"type": "boolean"},
			},
		},
	}
}

func queueItemInputSchema() map[string]any {
	return objectSchema(
		"Jenkins queue-item input.",
		map[string]any{
			"queueId": positiveSafeIntegerSchema("The Jenkins queue item ID."),
		},
		[]string{"queueId"},
	)
}

func numberedBuildInputSchema() map[string]any {
	return objectSchema(
		"Jenkins numbered-build input.",
		map[string]any{
			"jobPath": jobPathSchema(false),
			"buildNumber": positiveSafeIntegerSchema(
				"The positive Jenkins build number."),
		},
		[]string{"jobPath", "buildNumber"},
	)
}

func consoleLogInputSchema() map[string]any {
	return objectSchema(
		"Jenkins progressive console-log input.",
		map[string]any{
			"jobPath": jobPathSchema(false),
			"buildNumber": positiveSafeIntegerSchema(
				"The positive Jenkins build number."),
			"start": nonNegativeSafeIntegerSchema(
				"The byte offset at which to start the progressive log read."),
		},
		[]string{"jobPath", "buildNumber"},
	)
}

func consoleLogOutputSchema() map[string]any {
	return objectSchema(
		"One bounded Jenkins progressive console-log chunk.",
		map[string]any{
			"text": map[string]any{
				"type":        "string",
				"description": "The bounded console-log text chunk.",
			},
			"nextStart": nonNegativeSafeIntegerSchema(
				"The byte offset to use for the next progressive log read."),
			"moreData": map[string]any{
				"type":        "boolean",
				"description": "Whether Jenkins reports that more console output is available.",
			},
			"cursorReset": map[string]any{
				"type":        "boolean",
				"description": "Whether Jenkins reset the progressive byte cursor, for example after log rollover.",
			},
		},
		[]string{"text", "nextStart", "moreData", "cursorReset"},
	)
}

func acknowledgementOutputSchema(property string) map[string]any {
	return objectSchema(
		"Normalized Jenkins command acknowledgement.",
		map[string]any{
			property: map[string]any{
				"type":  "boolean",
				"const": true,
			},
		},
		[]string{property},
	)
}
