import { z } from 'zod'
import { IconNameSchema, ModelIdSchema } from './shared'
import { NodeIdStringSchema } from './systems'
import { RoleIdSchema } from './roles'
import { OntologyIdSchema } from '../ontology'
import { defineDomainRecord } from '../helpers'

// ---------------------------------------------------------------------------
// KnowledgeLink — a typed graph link pointing to another OM node.
// Uses NodeIdStringSchema (kind:dotted-path) to resolve to a graph node.
// Phase 1 only emits 'governs' edges from knowledge nodes.
// ---------------------------------------------------------------------------

export const KnowledgeTargetKindSchema = z
  .enum([
    'system',
    'client',
    'resource',
    'knowledge',
    'stage',
    'action',
    'role',
    'goal',
    'customer-segment',
    'offering',
    'ontology'
  ])
  .meta({ label: 'Target kind' })

export const KnowledgeTargetRefSchema = z
  .object({
    kind: KnowledgeTargetKindSchema,
    // Ontology targets use the canonical '<scope>:<kind>/<local-id>' ontology id format.
    // Business-logic validation of target existence is done in OrganizationModelSchema.superRefine.
    id: z.string().trim().min(1).max(300)
  })
  .superRefine((target, ctx) => {
    if (target.kind !== 'ontology') return

    const result = OntologyIdSchema.safeParse(target.id)
    if (!result.success) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['id'],
        message: 'Ontology knowledge targets must use <system-path>:<kind>/<local-id> or global:<kind>/<local-id>'
      })
    }
  })

const LegacyKnowledgeLinkSchema = z
  .object({
    nodeId: z.union([NodeIdStringSchema, z.templateLiteral(['ontology:', OntologyIdSchema])])
  })
  .superRefine((link, ctx) => {
    const [kind] = link.nodeId.split(':')
    if (!KnowledgeTargetKindSchema.safeParse(kind).success) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['nodeId'],
        message: `Unknown knowledge target kind "${kind}"`
      })
    }
  })

const CanonicalKnowledgeLinkSchema = z.object({
  target: KnowledgeTargetRefSchema
})

function nodeIdFromTarget(target: z.infer<typeof KnowledgeTargetRefSchema>): string {
  return `${target.kind}:${target.id}`
}

function targetFromNodeId(nodeId: string): z.infer<typeof KnowledgeTargetRefSchema> {
  const [kind, ...idParts] = nodeId.split(':')
  return {
    kind: KnowledgeTargetKindSchema.parse(kind),
    id: idParts.join(':')
  }
}

export const KnowledgeLinkSchema = z
  .union([CanonicalKnowledgeLinkSchema, LegacyKnowledgeLinkSchema])
  .transform((link) => {
    const target = 'target' in link ? link.target : targetFromNodeId(link.nodeId)
    return {
      target,
      nodeId: nodeIdFromTarget(target)
    }
  })

// ---------------------------------------------------------------------------
// OrgKnowledgeNode — single schema, `kind` discriminator drives presentation.
// Phase 1: body is raw MDX string (lossless migration from operations/ docs).
// Phase 2 (deferred): structured block format (Supabase-backed authoring).
// ---------------------------------------------------------------------------

export const OrgKnowledgeKindSchema = z
  .enum(['playbook', 'strategy', 'reference'])
  .meta({ label: 'Knowledge kind', color: 'grape' })

export const OrgKnowledgeNodeSchema = z.object({
  id: ModelIdSchema,
  kind: OrgKnowledgeKindSchema,
  title: z.string().trim().min(1).max(200),
  summary: z.string().trim().min(1).max(1000),
  icon: IconNameSchema.optional(),
  /** Canonical documentation URL when body content is a local summary. */
  externalUrl: z.string().trim().url().max(500).optional(),
  /** Optional generated source file path for local MDX-backed knowledge nodes. */
  sourceFilePath: z.string().trim().min(1).max(500).optional(),
  /** Raw MDX string. Phase 2 will introduce a structured block format. */
  body: z.string().trim().min(1),
  /**
   * Graph links to other OM nodes this knowledge node governs.
   * Each link emits a `governs` edge: knowledge-node -> target node.
   */
  links: z.array(KnowledgeLinkSchema).default([]),
  /** Role identifiers that own this knowledge node. */
  ownerIds: z.array(RoleIdSchema.meta({ ref: 'role' })).default([]),
  /** ISO date string (YYYY-MM-DD or full ISO 8601) of last meaningful update. */
  updatedAt: z.string().trim().min(1).max(50)
})

// ---------------------------------------------------------------------------
// Domain schema — flat Record<id, KnowledgeNode> (D3: flatten per Phase 4 cut)
//
// Wave 1 shape change: the old wrapper object { version, lastModified, nodes[] }
// is replaced by a plain id-keyed map. version/lastModified move to
// domainMetadata.knowledge (D7). The default is an empty map {}.
//
// Wave 2 (elevasis-core canonicalOrganizationModel) must produce a value of this
// shape: Record<KnowledgeId, OrgKnowledgeNode> — i.e., an object keyed by
// each node's id string, with OrgKnowledgeNode as the value. Example:
//   knowledge: {
//     'playbook.crm.discovery': { id: 'playbook.crm.discovery', kind: 'playbook', ... },
//   }
// ---------------------------------------------------------------------------

export const KnowledgeDomainSchema = z.record(ModelIdSchema, OrgKnowledgeNodeSchema).default({})

/** Validate and return a single knowledge node entry. */
export function defineKnowledgeNode(entry: z.input<typeof OrgKnowledgeNodeSchema>): OrgKnowledgeNode {
  return OrgKnowledgeNodeSchema.parse(entry)
}

/** Validate and return an id-keyed map of knowledge node entries. */
export function defineKnowledgeNodes(
  entries: readonly z.input<typeof OrgKnowledgeNodeSchema>[]
): Record<string, OrgKnowledgeNode> {
  return defineDomainRecord(OrgKnowledgeNodeSchema, entries)
}

export type OrgKnowledgeNode = z.infer<typeof OrgKnowledgeNodeSchema>
export type OrgKnowledgeNodeInput = z.input<typeof OrgKnowledgeNodeSchema>
export type OrgKnowledgeKind = z.infer<typeof OrgKnowledgeKindSchema>
export type KnowledgeTargetKind = z.infer<typeof KnowledgeTargetKindSchema>
export type KnowledgeTargetRef = z.infer<typeof KnowledgeTargetRefSchema>
export type KnowledgeLink = z.infer<typeof KnowledgeLinkSchema>
export type KnowledgeDomain = z.infer<typeof KnowledgeDomainSchema>
