/**
 * Migration helpers
 *
 * External signatures are intentionally stable. Internally these helpers now
 * read first-class ontology catalog records.
 */

import type { OrganizationModel } from './types'
import type { z } from 'zod'
import type { LeadGenStageCatalogEntry } from './catalogs/lead-gen'
import type { SalesPipelineSchema, SalesStageSchema } from './domains/sales'
import type { ProspectingBuildTemplateSchema, ProspectingLifecycleStageSchema } from './domains/prospecting'
import type { ProjectsDomainStateSchema } from './domains/projects'
import { compileOrganizationOntology, parseOntologyId, type OntologyCatalogType, type OntologyId } from './ontology'

type Pipeline = z.infer<typeof SalesPipelineSchema>
type Stage = z.infer<typeof SalesStageSchema>
type BuildTemplate = z.infer<typeof ProspectingBuildTemplateSchema>
type ProspectingStage = z.infer<typeof ProspectingLifecycleStageSchema>
type ProjectStatus = z.infer<typeof ProjectsDomainStateSchema>

type CatalogRecord = OntologyCatalogType & { origin?: { systemPath?: string } }
type CatalogEntry = Record<string, unknown>

function catalogRecords(model: OrganizationModel): CatalogRecord[] {
  return Object.values(compileOrganizationOntology(model).ontology.catalogTypes) as CatalogRecord[]
}

function localId(id: string): string {
  return parseOntologyId(id).localId
}

function systemScope(id: string, fallback?: string): string {
  return parseOntologyId(id).scope || fallback || ''
}

function entriesOf(catalog: CatalogRecord): Array<[string, CatalogEntry]> {
  return Object.entries(catalog.entries ?? {}).map(([id, value]) => [
    id,
    value && typeof value === 'object' && !Array.isArray(value) ? (value as CatalogEntry) : {}
  ])
}

function stringValue(value: unknown): string | undefined {
  return typeof value === 'string' ? value : undefined
}

function stringArray(value: unknown): string[] {
  return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []
}

function numberValue(value: unknown, fallback = 0): number {
  return typeof value === 'number' ? value : fallback
}

function stageFromEntry(entryId: string, entry: CatalogEntry): Stage {
  return {
    id: entryId,
    label: stringValue(entry.label) ?? entryId,
    order: numberValue(entry.order),
    semanticClass: (stringValue(entry.semanticClass) as Stage['semanticClass']) ?? 'open',
    surfaceIds: stringArray(entry.surfaceIds),
    resourceIds: stringArray(entry.resourceIds),
    color: stringValue(entry.color)
  }
}

function appliesToLocalId(catalog: CatalogRecord): string | undefined {
  const appliesTo = catalog.appliesTo
  if (appliesTo === undefined) return undefined
  return parseOntologyId(appliesTo).localId
}

function appliesToEntityKind(catalog: CatalogRecord): 'company' | 'contact' | 'project' | 'milestone' | 'task' | undefined {
  const id = appliesToLocalId(catalog)
  if (id === 'company' || id === 'contact' || id === 'project' || id === 'milestone' || id === 'task') return id
  if (id === 'deal') return undefined
  return undefined
}

export function getAllPipelines(model: OrganizationModel): Array<{ systemPath: string; pipeline: Pipeline }> {
  return catalogRecords(model)
    .filter((catalog) => catalog.kind === 'pipeline')
    .map((catalog) => {
      const pipeline: Pipeline = {
        id: localId(catalog.id),
        label: catalog.label ?? localId(catalog.id),
        ...(catalog.description ? { description: catalog.description } : {}),
        entityId: appliesToLocalId(catalog) ?? '',
        stages: entriesOf(catalog)
          .map(([entryId, entry]) => stageFromEntry(entryId, entry))
          .sort((a, b) => a.order - b.order || a.id.localeCompare(b.id))
      }

      return { systemPath: catalog.ownerSystemId ?? systemScope(catalog.id), pipeline }
    })
    .sort((a, b) => a.systemPath.localeCompare(b.systemPath) || a.pipeline.id.localeCompare(b.pipeline.id))
}

export function getStagesInPipeline(model: OrganizationModel, systemPath: string, pipelineLocalId: string): Stage[] {
  const catalog = catalogRecords(model).find(
    (record) =>
      record.kind === 'pipeline' &&
      (record.ownerSystemId ?? systemScope(record.id)) === systemPath &&
      localId(record.id) === pipelineLocalId
  )

  if (catalog === undefined) return []

  return entriesOf(catalog)
    .map(([entryId, entry]) => stageFromEntry(entryId, entry))
    .sort((a, b) => a.order - b.order || a.id.localeCompare(b.id))
}

export function getAllBuildTemplates(model: OrganizationModel): BuildTemplate[] {
  const catalogs = catalogRecords(model)
  const stepCatalogs = new Map<OntologyId, CatalogRecord>(
    catalogs.filter((catalog) => catalog.kind === 'template-step').map((catalog) => [catalog.id as OntologyId, catalog])
  )

  return catalogs
    .filter((catalog) => catalog.kind === 'template')
    .flatMap((catalog) =>
      entriesOf(catalog).map(([templateId, templateEntry]) => {
        const stepCatalogId = stringValue(templateEntry.stepCatalog) as OntologyId | undefined
        const stepCatalog = stepCatalogId !== undefined ? stepCatalogs.get(stepCatalogId) : undefined
        const steps =
          stepCatalog === undefined
            ? []
            : entriesOf(stepCatalog).sort(
                ([leftId, left], [rightId, right]) =>
                  numberValue(left.order, Number.MAX_SAFE_INTEGER) -
                    numberValue(right.order, Number.MAX_SAFE_INTEGER) || leftId.localeCompare(rightId)
              )

        return {
          order: numberValue(templateEntry.order, Number.MAX_SAFE_INTEGER),
          template: {
            id: templateId,
            label: stringValue(templateEntry.label) ?? templateId,
            ...(stringValue(templateEntry.description) ? { description: stringValue(templateEntry.description) } : {}),
            ...(stringValue(templateEntry.color) ? { color: stringValue(templateEntry.color) } : {}),
            steps: steps.map(([stepId, step]) => ({
              id: stepId,
              label: stringValue(step.label) ?? stepId,
              ...(stringValue(step.description) ? { description: stringValue(step.description) } : {}),
              ...step
            })) as BuildTemplate['steps']
          } satisfies BuildTemplate
        }
      })
    )
    .sort((a, b) => a.order - b.order || a.template.id.localeCompare(b.template.id))
    .map(({ template }) => template)
}

export function getAllProspectingStages(model: OrganizationModel, kind: 'company' | 'contact'): ProspectingStage[] {
  return catalogRecords(model)
    .filter((catalog) => catalog.kind === 'stage' && appliesToEntityKind(catalog) === kind)
    .flatMap((catalog) =>
      entriesOf(catalog).map(([entryId, entry]) => ({
        id: entryId,
        label: stringValue(entry.label) ?? entryId,
        order: numberValue(entry.order),
        ...(stringValue(entry.color) ? { color: stringValue(entry.color) } : {}),
        ...(stringValue(entry.description) ? { description: stringValue(entry.description) } : {})
      }))
    )
    .sort((a, b) => a.order - b.order || a.id.localeCompare(b.id))
}

export function getLeadGenStageCatalog(model: OrganizationModel): Record<string, LeadGenStageCatalogEntry> {
  const results: Record<string, LeadGenStageCatalogEntry> = {}

  for (const catalog of catalogRecords(model).filter(
    (record) => record.kind === 'stage' && (record.ownerSystemId ?? systemScope(record.id)) === 'sales.lead-gen'
  )) {
    const catalogEntity = appliesToEntityKind(catalog)
    if (catalogEntity !== 'company' && catalogEntity !== 'contact') continue

    for (const [entryId, entry] of entriesOf(catalog)) {
      const entity =
        entry.entity === 'contact' ? 'contact' : entry.entity === 'company' ? 'company' : catalogEntity
      const additionalEntities = stringArray(entry.additionalEntities).filter(
        (item): item is 'company' | 'contact' => item === 'company' || item === 'contact'
      )
      const recordEntity =
        entry.recordEntity === 'company' || entry.recordEntity === 'contact' ? entry.recordEntity : undefined
      const recordStageKey = stringValue(entry.recordStageKey)

      results[entryId] = {
        key: entryId,
        label: stringValue(entry.label) ?? entryId,
        description: stringValue(entry.description) ?? '',
        order: numberValue(entry.order),
        entity,
        ...(additionalEntities.length > 0 ? { additionalEntities } : {}),
        ...(recordEntity ? { recordEntity } : {}),
        ...(recordStageKey ? { recordStageKey } : {})
      }
    }
  }

  return Object.fromEntries(
    Object.entries(results).sort(([, a], [, b]) => a.order - b.order || a.key.localeCompare(b.key))
  )
}

export function getAllProjectStatuses(
  model: OrganizationModel,
  appliesTo: 'project' | 'milestone' | 'task'
): ProjectStatus[] {
  return catalogRecords(model)
    .filter((catalog) => catalog.kind === 'status-flow' && appliesToEntityKind(catalog) === appliesTo)
    .flatMap((catalog) =>
      entriesOf(catalog).map(([entryId, entry]) => ({
        id: entryId,
        label: stringValue(entry.label) ?? entryId,
        order: numberValue(entry.order),
        ...(stringValue(entry.color) ? { color: stringValue(entry.color) } : {}),
        ...(stringValue(entry.description) ? { description: stringValue(entry.description) } : {})
      }))
    )
    .sort((a, b) => a.order - b.order || a.id.localeCompare(b.id))
}
