import type {
  MentionSuggestionProvider,
  PromptSuggestion,
} from '@simon_he/vue-tui'
import { execFile } from 'node:child_process'
import { promisify } from 'node:util'
import { buildCommitMentionValue } from '#cli/core/commit-mentions'

type CommitEntry = Readonly<{
  sha: string
  author: string
  date: string
  subject: string
}>

interface CommitCacheEntry {
  expiresAt: number
  commits: readonly CommitEntry[]
  inFlight?: Promise<readonly CommitEntry[]>
}

const execFileAsync = promisify(execFile)
const DEFAULT_RECENT_MAX_COMMITS = 200
const DEFAULT_CACHE_TTL_MS = 6_000
const DEFAULT_TIMEOUT_MS = 1_200
const DEFAULT_MAX_BUFFER = 8 * 1024 * 1024

function parseCommitLog(output: string): readonly CommitEntry[] {
  const lines = String(output || '')
    .split('\n')
    .filter(Boolean)
  if (lines.length === 0) return []
  const commits: CommitEntry[] = []
  for (const line of lines) {
    const [sha, author, date, ...rest] = line.split('\t')
    const subject = rest.join('\t').trim()
    if (!sha) continue
    commits.push({
      sha: sha.trim(),
      author: String(author ?? '').trim(),
      date: String(date ?? '').trim(),
      subject,
    })
  }
  return commits
}

async function loadGitCommits(
  workspace: string,
  maxCommits: number,
  timeoutMs: number,
): Promise<readonly CommitEntry[]> {
  if (!workspace) return []
  const args = [
    '-C',
    workspace,
    'log',
    '--no-color',
    '--all',
    '--pretty=format:%H%x09%an%x09%ad%x09%s',
    '--date=short',
  ]
  if (maxCommits > 0) {
    args.push('-n', String(maxCommits))
  }
  const { stdout } = await execFileAsync('git', args, {
    timeout: timeoutMs,
    maxBuffer: DEFAULT_MAX_BUFFER,
  })
  return parseCommitLog(stdout)
}

function buildCacheKey(workspace: string, mode: 'recent' | 'full'): string {
  return `${workspace}::${mode}`
}

async function getCachedCommits(
  cache: Map<string, CommitCacheEntry>,
  workspace: string,
  mode: 'recent' | 'full',
  maxCommits: number,
  cacheTtlMs: number,
  timeoutMs: number,
): Promise<readonly CommitEntry[]> {
  const key = buildCacheKey(workspace, mode)
  const now = Date.now()
  const cached = cache.get(key)
  if (cached?.inFlight) return cached.inFlight
  if (cached && cached.expiresAt > now) return cached.commits

  const inFlight = loadGitCommits(workspace, maxCommits, timeoutMs).catch(
    () => [],
  )
  cache.set(key, {
    commits: cached?.commits ?? [],
    expiresAt: now + cacheTtlMs,
    inFlight,
  })
  const commits = await inFlight
  cache.set(key, { commits, expiresAt: Date.now() + cacheTtlMs })
  return commits
}

export function createGitCommitMentionProvider(
  opts: Readonly<{
    recentMaxCommits?: number
    cacheTtlMs?: number
    timeoutMs?: number
  }> = {},
): MentionSuggestionProvider {
  const recentMaxCommits = Math.max(
    1,
    Math.floor(opts.recentMaxCommits ?? DEFAULT_RECENT_MAX_COMMITS),
  )
  const cacheTtlMs = Math.max(
    500,
    Math.floor(opts.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS),
  )
  const timeoutMs = Math.max(
    200,
    Math.floor(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS),
  )
  const cache = new Map<string, CommitCacheEntry>()

  return async (info) => {
    const workspace = String(info.workspace || '').trim()
    if (!workspace || info.maxItems <= 0) return []

    const q = info.query.trim().toLowerCase()
    const mode = q && looksLikeHashQuery(q) ? 'full' : 'recent'
    const maxCommits = mode === 'recent' ? recentMaxCommits : 0
    let commits = await getCachedCommits(
      cache,
      workspace,
      mode,
      maxCommits,
      cacheTtlMs,
      timeoutMs,
    )
    if (mode === 'full' && commits.length === 0) {
      commits = await getCachedCommits(
        cache,
        workspace,
        'recent',
        recentMaxCommits,
        cacheTtlMs,
        timeoutMs,
      )
    }
    if (!commits.length) return []

    let filtered = commits
    if (q) {
      filtered = commits.filter((c) => c.sha.startsWith(q))
      if (!filtered.length) filtered = commits.filter((c) => c.sha.includes(q))
    }

    const limit = Math.max(0, Math.floor(info.maxItems))
    return filtered.slice(0, limit).map<PromptSuggestion>((commit) => ({
      value: `${info.trigger}${commit.sha}`,
      insert: `${info.trigger}${commit.sha} `,
      detail: [commit.author, commit.date, commit.subject]
        .filter(Boolean)
        .join(' | '),
      mentionValue: buildCommitMentionValue(commit.sha),
      mentionBehavior: 'collect',
    }))
  }
}
const HASH_QUERY_RE = /^[0-9a-f]+$/i

function looksLikeHashQuery(query: string): boolean {
  const q = String(query || '').trim()
  if (q.length < 3) return false
  return HASH_QUERY_RE.test(q)
}
