import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, realpathSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'

import {
  WORKLEAF_PREFIX,
  WORKLEAF_BRANCH_SEPARATOR,
  workleafDir,
  workleafBranchName,
  parentOfWorkleaf,
  detectWorkleafContext,
  listWorkleafs,
} from './paths.js'

describe('workleafBranchName', () => {
  it('builds <parent>--workleaf--<slug>', () => {
    expect(workleafBranchName('main', 'quick-fix')).toBe('main--workleaf--quick-fix')
    expect(workleafBranchName('4.3.0', 'Quick Fix')).toBe('4.3.0--workleaf--quick-fix')
  })

  it('slugifies the name', () => {
    expect(workleafBranchName('main', 'My Cool.Feature')).toBe('main--workleaf--my-cool-feature')
  })
})

describe('parentOfWorkleaf', () => {
  it('splits a normal workleaf branch', () => {
    expect(parentOfWorkleaf('main--workleaf--quick-fix')).toEqual({ parent: 'main', slug: 'quick-fix' })
    expect(parentOfWorkleaf('4.3.0--workleaf--bench')).toEqual({ parent: '4.3.0', slug: 'bench' })
  })

  it('returns null for non-workleaf branches', () => {
    expect(parentOfWorkleaf('main')).toBeNull()
    expect(parentOfWorkleaf('4.3.0--feature')).toBeNull()
    expect(parentOfWorkleaf('')).toBeNull()
  })

  it('returns null for malformed inputs', () => {
    expect(parentOfWorkleaf(null)).toBeNull()
    expect(parentOfWorkleaf(undefined)).toBeNull()
    expect(parentOfWorkleaf(123)).toBeNull()
  })

  it('uses the LAST --workleaf-- separator (parent names containing it)', () => {
    // A pathological but possible parent name: someone named their parent
    // worktree `something--workleaf--child`. We should still split it correctly.
    expect(parentOfWorkleaf('something--workleaf--child--workleaf--slug')).toEqual({
      parent: 'something--workleaf--child',
      slug: 'slug',
    })
  })

  it('returns null when parent or slug ends up empty', () => {
    expect(parentOfWorkleaf('--workleaf--slug')).toBeNull()
    expect(parentOfWorkleaf('parent--workleaf--')).toBeNull()
  })
})

describe('workleafDir', () => {
  let tempRoot
  let originalCwd

  beforeEach(() => {
    tempRoot = realpathSync(mkdtempSync(join(tmpdir(), 'sb-workleaf-dir-')))
    originalCwd = process.cwd()
    process.chdir(tempRoot)
  })

  afterEach(() => {
    process.chdir(originalCwd)
    rmSync(tempRoot, { recursive: true, force: true })
  })

  it('returns workleafs/<parent>/workleaf--<slug> when called from repo root', () => {
    expect(workleafDir('main', 'quick-fix')).toBe(
      join(tempRoot, 'workleafs', 'main', 'workleaf--quick-fix')
    )
  })

  it('slugifies the workleaf name', () => {
    expect(workleafDir('main', 'My Cool.Feature')).toBe(
      join(tempRoot, 'workleafs', 'main', 'workleaf--my-cool-feature')
    )
  })

  it('resolves repo root even when called from inside a worktree', () => {
    const wt = join(tempRoot, 'worktrees', '4.3.0')
    mkdirSync(wt, { recursive: true })
    expect(workleafDir('4.3.0', 'fix', wt)).toBe(
      join(tempRoot, 'workleafs', '4.3.0', 'workleaf--fix')
    )
  })

  it('resolves repo root even when called from inside a workleaf', () => {
    const wl = join(tempRoot, 'workleafs', '4.3.0', 'workleaf--outer')
    mkdirSync(wl, { recursive: true })
    expect(workleafDir('4.3.0', 'inner', wl)).toBe(
      join(tempRoot, 'workleafs', '4.3.0', 'workleaf--inner')
    )
  })
})

describe('detectWorkleafContext', () => {
  let tempRoot

  beforeEach(() => {
    tempRoot = realpathSync(mkdtempSync(join(tmpdir(), 'sb-workleaf-ctx-')))
  })

  afterEach(() => {
    rmSync(tempRoot, { recursive: true, force: true })
  })

  it('returns inWorkleaf=false from repo root', () => {
    expect(detectWorkleafContext(tempRoot).inWorkleaf).toBe(false)
  })

  it('returns inWorkleaf=false from inside a worktree', () => {
    const wt = join(tempRoot, 'worktrees', '4.3.0')
    mkdirSync(wt, { recursive: true })
    expect(detectWorkleafContext(wt).inWorkleaf).toBe(false)
  })

  it('detects parent=main when cwd is inside a main-parented workleaf', () => {
    const dir = join(tempRoot, 'workleafs', 'main', 'workleaf--fix')
    mkdirSync(dir, { recursive: true })
    const ctx = detectWorkleafContext(dir)
    expect(ctx.inWorkleaf).toBe(true)
    expect(ctx.parent).toBe('main')
    expect(ctx.slug).toBe('fix')
    expect(ctx.branch).toBe('main--workleaf--fix')
    expect(ctx.dir).toBe(dir)
    expect(ctx.root).toBe(tempRoot)
  })

  it('detects parent=<worktree> when cwd is inside a worktree-parented workleaf', () => {
    const dir = join(tempRoot, 'workleafs', '4.3.0', 'workleaf--bench')
    mkdirSync(dir, { recursive: true })
    const ctx = detectWorkleafContext(dir)
    expect(ctx.inWorkleaf).toBe(true)
    expect(ctx.parent).toBe('4.3.0')
    expect(ctx.slug).toBe('bench')
    expect(ctx.branch).toBe('4.3.0--workleaf--bench')
    expect(ctx.root).toBe(tempRoot)
  })

  it('detects context from a nested cwd inside a workleaf', () => {
    const dir = join(tempRoot, 'workleafs', 'main', 'workleaf--fix', 'src', 'nested')
    mkdirSync(dir, { recursive: true })
    const ctx = detectWorkleafContext(dir)
    expect(ctx.inWorkleaf).toBe(true)
    expect(ctx.parent).toBe('main')
    expect(ctx.slug).toBe('fix')
  })

  it('does not match a directory that lacks the workleaf-- prefix', () => {
    const dir = join(tempRoot, 'workleafs', 'main', 'not-a-workleaf')
    mkdirSync(dir, { recursive: true })
    expect(detectWorkleafContext(dir).inWorkleaf).toBe(false)
  })
})

describe('listWorkleafs', () => {
  let tempRoot

  beforeEach(() => {
    tempRoot = realpathSync(mkdtempSync(join(tmpdir(), 'sb-workleaf-list-')))
  })

  afterEach(() => {
    rmSync(tempRoot, { recursive: true, force: true })
  })

  it('returns [] when workleafs/ does not exist', () => {
    expect(listWorkleafs(tempRoot)).toEqual([])
  })

  it('returns [] when workleafs/ has no valid entries', () => {
    mkdirSync(join(tempRoot, 'workleafs'), { recursive: true })
    expect(listWorkleafs(tempRoot)).toEqual([])
  })

  it('lists workleafs that have a .git marker', () => {
    const dir = join(tempRoot, 'workleafs', 'main', 'workleaf--fix')
    mkdirSync(dir, { recursive: true })
    writeFileSync(join(dir, '.git'), 'gitdir: /fake')

    const result = listWorkleafs(tempRoot)
    expect(result).toEqual([
      { parent: 'main', slug: 'fix', branch: 'main--workleaf--fix', dir },
    ])
  })

  it('skips entries missing the workleaf-- prefix and entries without .git', () => {
    mkdirSync(join(tempRoot, 'workleafs', 'main', 'workleaf--ok'), { recursive: true })
    writeFileSync(join(tempRoot, 'workleafs', 'main', 'workleaf--ok', '.git'), 'gitdir: /fake')
    // Missing .git marker
    mkdirSync(join(tempRoot, 'workleafs', 'main', 'workleaf--no-git'), { recursive: true })
    // Missing prefix
    mkdirSync(join(tempRoot, 'workleafs', 'main', 'just-a-folder'), { recursive: true })

    const result = listWorkleafs(tempRoot)
    expect(result).toHaveLength(1)
    expect(result[0].slug).toBe('ok')
  })

  it('groups workleafs across multiple parents', () => {
    const a = join(tempRoot, 'workleafs', 'main', 'workleaf--alpha')
    const b = join(tempRoot, 'workleafs', '4.3.0', 'workleaf--beta')
    mkdirSync(a, { recursive: true })
    mkdirSync(b, { recursive: true })
    writeFileSync(join(a, '.git'), 'gitdir: /fake')
    writeFileSync(join(b, '.git'), 'gitdir: /fake')

    const result = listWorkleafs(tempRoot)
    const parents = result.map((r) => r.parent).sort()
    expect(parents).toEqual(['4.3.0', 'main'])
  })
})

describe('constants', () => {
  it('exposes WORKLEAF_PREFIX', () => {
    expect(WORKLEAF_PREFIX).toBe('workleaf--')
  })

  it('exposes WORKLEAF_BRANCH_SEPARATOR', () => {
    expect(WORKLEAF_BRANCH_SEPARATOR).toBe('--workleaf--')
  })
})
