/** LTM procedural memory — user coding preferences and patterns (English, LLM-to-LLM) */

import * as Log from "@saeeol/core/util/log"
import * as Embedder from "@/provider/local/embedder"
import type { Memory } from "@/ltm/types"

const log = Log.create({ service: "ltm/memory/procedural" })

/** A detected coding style signal */
interface StyleSignal {
  language: string
  pattern: string
  evidence: string
}

/** Extract coding style signals from a file edit */
export function extractStyleSignals(
  filePath: string,
  content: string,
): StyleSignal[] {
  const signals: StyleSignal[] = []
  const ext = filePath.split(".").pop() ?? ""
  if (content.includes("//") && (ext === "ts" || ext === "js")) {
    const hasKoreanComment = /[ㄱ-ㅎㅏ-ㅣ가-힣]/.test(content)
    signals.push({
      language: ext,
      pattern: "comment-language",
      evidence: hasKoreanComment ? "comments in Korean" : "comments in English",
    })
  }
  if (content.includes("  ") && !content.includes("\t")) {
    signals.push({ language: ext, pattern: "indent", evidence: "2-space indentation" })
  } else if (content.includes("\t")) {
    signals.push({ language: ext, pattern: "indent", evidence: "tab indentation" })
  }
  if (ext === "ts" || ext === "js") {
    const hasSemicolons = /;\s*\n/.test(content)
    signals.push({
      language: ext,
      pattern: "semicolons",
      evidence: hasSemicolons ? "uses semicolons" : "no semicolons",
    })
  }
  const singleQuotes = (content.match(/'/g) ?? []).length
  const doubleQuotes = (content.match(/"/g) ?? []).length
  if (singleQuotes > doubleQuotes * 2) {
    signals.push({ language: ext, pattern: "quotes", evidence: "prefers single quotes" })
  } else if (doubleQuotes > singleQuotes * 2) {
    signals.push({ language: ext, pattern: "quotes", evidence: "prefers double quotes" })
  }
  const camelCase = (content.match(/[a-z][A-Z]/g) ?? []).length
  const snakeCase = (content.match(/_[a-z]/g) ?? []).length
  if (camelCase > snakeCase * 3) {
    signals.push({ language: ext, pattern: "naming", evidence: "camelCase naming" })
  } else if (snakeCase > camelCase * 3) {
    signals.push({ language: ext, pattern: "naming", evidence: "snake_case naming" })
  }

  return signals
}

/** Convert style signals into procedural memories */
export async function fromStyleSignals(
  projectID: string | undefined,
  signals: StyleSignal[],
): Promise<Memory[]> {
  const memories: Memory[] = []

  for (const signal of signals) {
    const summary = `user coding style: ${signal.language} ${signal.pattern} = ${signal.evidence}`
    try {
      const vector = await Embedder.embedOne(summary)
      memories.push({
        id: `proc:style:${signal.language}:${signal.pattern}`,
        type: "procedural",
        content: signal.evidence,
        summary,
        vector,
        metadata: {
          source: `style:${signal.language}`,
          timestamp: Date.now(),
          projectID,
          tags: ["style", signal.language, signal.pattern],
        },
      })
    } catch (e) {
      log.error("failed to create procedural memory", { error: e, pattern: signal.pattern })
    }
  }

  return memories
}