import { z } from 'zod'
import { EntityIdSchema } from './entities'
import { DescriptionSchema, LabelSchema, ModelIdSchema } from './shared'
import { defineDomainRecord } from '../helpers'

export const ActionResourceIdSchema = z
  .string()
  .trim()
  .min(1)
  .max(255)
  .regex(/^[A-Za-z0-9]+(?:[-._][A-Za-z0-9]+)*$/, 'Resource IDs must use letters, numbers, -, _, or . separators')

export const ActionInvocationKindSchema = z
  .enum(['slash-command', 'mcp-tool', 'api-endpoint', 'script-execution'])
  .meta({ label: 'Invocation kind' })

export const ActionIdSchema = ModelIdSchema

export const ActionScopeSchema = z.union([
  z.literal('global'),
  z.object({
    domain: ModelIdSchema
  })
])

export const ActionRefSchema = z.object({
  actionId: ActionIdSchema.meta({ ref: 'action' }),
  intent: z.enum(['exposes', 'consumes']).meta({ label: 'Intent' })
})

export const SlashCommandInvocationSchema = z.object({
  kind: z.literal('slash-command'),
  command: z
    .string()
    .trim()
    .min(1)
    .max(200)
    .regex(/^\/[^\s].*$/, 'Slash commands must start with /'),
  toolFactory: ModelIdSchema.optional()
})

export const McpToolInvocationSchema = z.object({
  kind: z.literal('mcp-tool'),
  server: ModelIdSchema,
  name: ModelIdSchema
})

export const ApiEndpointInvocationSchema = z.object({
  kind: z.literal('api-endpoint'),
  method: z.enum(['GET', 'POST', 'PATCH', 'DELETE']).meta({ label: 'HTTP method' }),
  path: z.string().trim().startsWith('/').max(500),
  requestSchema: ModelIdSchema.optional(),
  responseSchema: ModelIdSchema.optional()
})

export const ScriptExecutionInvocationSchema = z.object({
  kind: z.literal('script-execution'),
  resourceId: ActionResourceIdSchema
})

export const ActionInvocationSchema = z.discriminatedUnion('kind', [
  SlashCommandInvocationSchema,
  McpToolInvocationSchema,
  ApiEndpointInvocationSchema,
  ScriptExecutionInvocationSchema
])

export const ActionSchema = z.object({
  id: ActionIdSchema,
  /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
  order: z.number(),
  label: LabelSchema,
  description: DescriptionSchema.optional(),
  scope: ActionScopeSchema.default('global'),
  resourceId: ActionResourceIdSchema.optional(),
  affects: z.array(EntityIdSchema.meta({ ref: 'entity' })).optional(),
  invocations: z.array(ActionInvocationSchema).default([]),
  knowledge: z
    .array(ModelIdSchema.meta({ ref: 'knowledge' }))
    .default([])
    .optional(),
  lifecycle: z
    .enum(['draft', 'beta', 'active', 'deprecated', 'archived'])
    .meta({ label: 'Lifecycle', color: 'teal' })
    .default('active')
})

export const ActionsDomainSchema = z
  .record(z.string(), ActionSchema)
  .refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
    message: 'Each action entry id must match its map key'
  })
  .default({})

/**
 * Generic empty default for the actions domain.
 * Elevasis-specific action entries (LEAD_GEN_ACTION_ENTRIES, CRM_ACTION_ENTRIES,
 * DEFAULT_ORGANIZATION_MODEL_ACTIONS) have been relocated to
 * the tenant-owned organization model package.
 * Tenant OM configs supply their own action entries via `resolveOrganizationModel`.
 */
export const DEFAULT_ORGANIZATION_MODEL_ACTIONS: z.infer<typeof ActionsDomainSchema> = {}

export function findOrganizationActionById(
  id: string,
  actions: z.infer<typeof ActionsDomainSchema> = DEFAULT_ORGANIZATION_MODEL_ACTIONS
): z.infer<typeof ActionSchema> | undefined {
  return actions[id]
}

/** Validate and return a single action entry. */
export function defineAction(entry: z.input<typeof ActionSchema>): z.infer<typeof ActionSchema> {
  return ActionSchema.parse(entry)
}

/** Validate and return an id-keyed map of action entries. */
export function defineActions(
  entries: readonly z.input<typeof ActionSchema>[]
): Record<string, z.infer<typeof ActionSchema>> {
  return defineDomainRecord(ActionSchema, entries)
}

export type ActionId = z.infer<typeof ActionIdSchema>
export type ActionScope = z.infer<typeof ActionScopeSchema>
export type ActionRef = z.infer<typeof ActionRefSchema>
export type ActionInvocationKind = z.infer<typeof ActionInvocationKindSchema>
export type ActionInvocation = z.infer<typeof ActionInvocationSchema>
export type Action = z.infer<typeof ActionSchema>
export type ActionsDomain = z.infer<typeof ActionsDomainSchema>
