import { createHash } from 'node:crypto'
import type {
  SkillLearningProjectContext,
  SkillLearningScope,
  StoredSkillObservation,
} from './observationStore.js'
import type { Instinct as BaseInstinct, InstinctStatus } from './types.js'

export type { Instinct } from './types.js'

export type StoredInstinct = BaseInstinct & {
  observationIds?: string[]
}

export type InstinctCandidate = Omit<
  StoredInstinct,
  'id' | 'createdAt' | 'updatedAt' | 'status'
> & {
  id?: string
  status?: InstinctStatus
}

export function createInstinct(
  candidate: InstinctCandidate,
  now = new Date().toISOString(),
): StoredInstinct {
  return normalizeInstinct({
    id:
      candidate.id ??
      buildInstinctId(candidate.trigger, candidate.action, candidate.scope),
    ...candidate,
    createdAt: now,
    updatedAt: now,
    status: candidate.status ?? 'pending',
  })
}

const MAX_EVIDENCE_ENTRIES = 10

export function normalizeInstinct(instinct: StoredInstinct): StoredInstinct {
  const uniqueEvidence = Array.from(new Set(instinct.evidence.filter(Boolean)))
  return {
    ...instinct,
    id: instinct.id || buildInstinctId(instinct.trigger, instinct.action),
    confidence: clampConfidence(instinct.confidence),
    evidence: uniqueEvidence.slice(-MAX_EVIDENCE_ENTRIES),
    evidenceOutcome: instinct.evidenceOutcome,
    observationIds: instinct.observationIds
      ? Array.from(new Set(instinct.observationIds)).slice(-20)
      : undefined,
  }
}

export function serializeInstinct(instinct: StoredInstinct): string {
  return `${JSON.stringify(normalizeInstinct(instinct), null, 2)}\n`
}

export function parseInstinct(content: string): StoredInstinct {
  return normalizeInstinct(JSON.parse(content) as StoredInstinct)
}

export function buildInstinctId(
  trigger: string,
  action: string,
  scope: SkillLearningScope = 'project',
): string {
  const slug = `${trigger} ${action}`
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-|-$/g, '')
    .slice(0, 48)
  const hash = createHash('sha1')
    .update(`${scope}\n${trigger}\n${action}`)
    .digest('hex')
    .slice(0, 10)
  return `${slug || 'instinct'}-${hash}`
}

export function candidateFromObservation(
  observation: StoredSkillObservation,
  project?: SkillLearningProjectContext,
): Partial<InstinctCandidate> {
  return {
    scope: project?.scope ?? 'project',
    projectId: project?.projectId ?? observation.projectId,
    projectName: project?.projectName ?? observation.projectName,
    source: 'session-observation',
    evidence: [
      observation.messageText ??
        observation.toolOutput ??
        observation.toolInput ??
        observation.toolName ??
        observation.id,
    ],
    observationIds: [observation.id],
  }
}

export function isContradictingInstinct(
  existing: StoredInstinct,
  incoming: StoredInstinct,
): boolean {
  const existingTrigger = existing.trigger.toLowerCase()
  const incomingTrigger = incoming.trigger.toLowerCase()
  if (existingTrigger !== incomingTrigger) return false

  const existingAction = existing.action.toLowerCase()
  const incomingAction = incoming.action.toLowerCase()
  return (
    existingAction.includes('avoid') !== incomingAction.includes('avoid') ||
    existingAction.includes('prefer') !== incomingAction.includes('prefer')
  )
}

export function clampConfidence(confidence: number): number {
  if (Number.isNaN(confidence)) return 0
  return Math.max(0, Math.min(1, Number(confidence.toFixed(2))))
}
