import { z } from 'zod'
import { ResourceIdSchema } from './resources'
import { ModelIdSchema } from './shared'
import { SystemIdSchema } from './systems'
import { defineDomainRecord } from '../helpers'

// ---------------------------------------------------------------------------
// Role schema - one entry per distinct role in the organization's chart.
// Inspired by the EOS Accountability Chart but uses plain-language field names
// throughout. No EOS jargon: "title" (not seatTitle), "responsibilities"
// (not accountabilities), "reportsToId", "heldBy".
//
// Cross-reference enforcement lives in `OrganizationModelSchema.superRefine()`:
// `reportsToId` must resolve to another Role, `responsibleFor` entries must
// resolve to Systems, and agent holders must resolve to Agent Resources.
// Role hierarchy cycle detection is also enforced at the model level.
// ---------------------------------------------------------------------------

export const RoleIdSchema = ModelIdSchema

export const HumanRoleHolderSchema = z.object({
  kind: z.literal('human'),
  userId: z.string().trim().min(1).max(200)
})

export const AgentRoleHolderSchema = z.object({
  kind: z.literal('agent'),
  agentId: ResourceIdSchema.meta({ ref: 'resource' })
})

export const TeamRoleHolderSchema = z.object({
  kind: z.literal('team'),
  memberIds: z.array(z.string().trim().min(1).max(200)).min(1)
})

export const RoleHolderSchema = z.discriminatedUnion('kind', [
  HumanRoleHolderSchema,
  AgentRoleHolderSchema,
  TeamRoleHolderSchema
])

export const RoleHoldersSchema = z.union([RoleHolderSchema, z.array(RoleHolderSchema).min(1)])

export const RoleSchema = z.object({
  /** Stable unique identifier for the role (e.g. "role-ceo", "role-head-of-sales"). */
  id: RoleIdSchema,
  /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
  order: z.number(),
  /** Human-readable title shown to agents and in UI (e.g. "CEO", "Head of Sales"). */
  title: z.string().trim().min(1).max(200),
  /**
   * List of responsibilities this role owns - plain-language descriptions of
   * what the person in this role is accountable for delivering.
   * Defaults to empty array so minimal role definitions stay concise.
   */
  responsibilities: z.array(z.string().trim().max(500)).default([]),
  /**
   * Optional: ID of another role this role reports to.
   * When present, must reference another `roles[].id` in the same organization.
   */
  reportsToId: RoleIdSchema.meta({ ref: 'role' }).optional(),
  /**
   * Optional: human, agent, or team holder currently filling this role.
   * Agent holders reference OM Resource IDs and are validated at the model level.
   */
  heldBy: RoleHoldersSchema.optional(),
  /**
   * Optional Systems this role is accountable for.
   * Cross-reference enforced in `OrganizationModelSchema.superRefine()`.
   */
  responsibleFor: z.array(SystemIdSchema.meta({ ref: 'system' })).optional()
})

// ---------------------------------------------------------------------------
// Roles domain schema - a collection of roles.
// ---------------------------------------------------------------------------

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

// ---------------------------------------------------------------------------
// Seed - empty by default; adapters populate with real role definitions.
// ---------------------------------------------------------------------------

export const DEFAULT_ORGANIZATION_MODEL_ROLES: z.infer<typeof RolesDomainSchema> = {}

/** Validate and return a single role entry. */
export function defineRole(entry: z.input<typeof RoleSchema>): z.infer<typeof RoleSchema> {
  return RoleSchema.parse(entry)
}

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

export type RoleId = z.infer<typeof RoleIdSchema>
export type HumanRoleHolder = z.infer<typeof HumanRoleHolderSchema>
export type AgentRoleHolder = z.infer<typeof AgentRoleHolderSchema>
export type TeamRoleHolder = z.infer<typeof TeamRoleHolderSchema>
export type RoleHolder = z.infer<typeof RoleHolderSchema>
export type Role = z.infer<typeof RoleSchema>
export type RolesDomain = z.infer<typeof RolesDomainSchema>
