# Service Schema - Service definitions for provisioning
# Version: 1.0.0

{
  # Port configuration for service containers
  Port = {
    container_port
      | Number
      | doc "Port number inside the container"
      | {
        label = "valid port",
        predicate = fun p => p >= 1 && p <= 65535
      },

    host_port
      | Number
      | doc "Port number on the host machine (1024-65535 for non-root)"
      | {
        label = "valid host port",
        predicate = fun p => p >= 1024 && p <= 65535
      },

    protocol
      | [| 'tcp, 'udp, 'sctp |]
      | doc "Network protocol"
      | default = 'tcp,
  },

  # Resource limits and requests
  Resources = {
    cpu_limit
      | String
      | doc "CPU limit (e.g., '1000m' for 1 core, '500m' for 0.5 cores)"
      | {
        label = "valid CPU limit",
        predicate = fun s =>
          std.string.is_match "^[0-9]+(\\.[0-9]+)?m?$" s
      }
      | optional,

    cpu_request
      | String
      | doc "CPU request (guaranteed CPU allocation)"
      | {
        label = "valid CPU request",
        predicate = fun s =>
          std.string.is_match "^[0-9]+(\\.[0-9]+)?m?$" s
      }
      | optional,

    memory_limit
      | String
      | doc "Memory limit (e.g., '512Mi', '1Gi')"
      | {
        label = "valid memory limit",
        predicate = fun s =>
          std.string.is_match "^[0-9]+(Mi|Gi|M|G|Ki|K)$" s
      }
      | optional,

    memory_request
      | String
      | doc "Memory request (guaranteed memory allocation)"
      | {
        label = "valid memory request",
        predicate = fun s =>
          std.string.is_match "^[0-9]+(Mi|Gi|M|G|Ki|K)$" s
      }
      | optional,
  },

  # Container image configuration
  Image = {
    name
      | String
      | doc "Image name (e.g., 'nginx', 'my-app')"
      | {
        label = "valid image name",
        predicate = fun s =>
          std.string.length s >= 1 && std.string.length s <= 256
      },

    tag
      | String
      | doc "Image tag (e.g., 'latest', '1.0.0', 'v2.3-alpine')"
      | {
        label = "valid image tag",
        predicate = fun s =>
          std.string.is_match "^[a-zA-Z0-9._-]+$" s
      }
      | default = "latest",

    build_context
      | String
      | doc "Build context path for building from Dockerfile"
      | optional,

    dockerfile
      | String
      | doc "Path to Dockerfile (relative to build_context)"
      | default = "Dockerfile",
  },

  # Health check configuration
  Healthcheck = {
    enabled
      | Bool
      | doc "Enable health check monitoring"
      | default = true,

    endpoint
      | String
      | doc "HTTP endpoint for health checks (e.g., '/health', '/api/v1/health')"
      | default = "/health",

    interval
      | Number
      | doc "Interval between health checks in seconds (5-300)"
      | {
        label = "valid interval",
        predicate = fun n => n >= 5 && n <= 300
      }
      | default = 30,

    timeout
      | Number
      | doc "Health check timeout in seconds (1-60)"
      | {
        label = "valid timeout",
        predicate = fun n => n >= 1 && n <= 60
      }
      | default = 10,

    retries
      | Number
      | doc "Number of consecutive failures before marking unhealthy (1-10)"
      | {
        label = "valid retries",
        predicate = fun n => n >= 1 && n <= 10
      }
      | default = 3,

    start_period
      | Number
      | doc "Grace period in seconds before health checks start (0-300)"
      | {
        label = "valid start period",
        predicate = fun n => n >= 0 && n <= 300
      }
      | default = 0,
  },

  # Main service definition
  Service = {
    type
      | [| 'api, 'worker, 'scheduler, 'frontend, 'backend, 'database, 'cache, 'queue |]
      | doc "Service type determining deployment characteristics",

    image
      | Image
      | doc "Container image configuration",

    ports
      | Array Port
      | doc "Port mappings for the service"
      | default = [],

    environment
      | {_: String}
      | doc "Environment variables as key-value pairs"
      | default = {},

    env_file
      | Array String
      | doc "Paths to environment files"
      | default = [],

    healthcheck
      | Healthcheck
      | doc "Health check configuration"
      | default = {enabled = true, endpoint = "/health"},

    resources
      | Resources
      | doc "Resource limits and requests"
      | optional,

    depends_on
      | Array String
      | doc "Service dependencies (names of other services)"
      | {
        label = "valid dependencies",
        predicate = fun deps =>
          std.array.all (fun s =>
            std.string.is_match "^[a-z0-9-]+$" s
          ) deps
      }
      | default = [],

    replicas
      | Number
      | doc "Number of service replicas to run (1-100)"
      | {
        label = "valid replica count",
        predicate = fun n => n >= 1 && n <= 100
      }
      | default = 1,

    restart_policy
      | [| 'no, 'always, 'on-failure, 'unless-stopped |]
      | doc "Container restart policy"
      | default = 'unless-stopped,

    command
      | Array String
      | doc "Override container command"
      | optional,

    entrypoint
      | Array String
      | doc "Override container entrypoint"
      | optional,

    working_dir
      | String
      | doc "Working directory inside container"
      | optional,

    user
      | String
      | doc "User to run container as (UID:GID or username)"
      | optional,

    volumes
      | Array String
      | doc "Volume mounts (format: 'host_path:container_path[:options]')"
      | default = [],

    networks
      | Array String
      | doc "Networks to attach the service to"
      | default = [],

    labels
      | {_: String}
      | doc "Container labels for metadata"
      | default = {},
  },
}
