import { describe, it, expect } from 'vitest'
import {
  parseEnvLocal,
  detectPrConflicts,
  formatPrSummary,
  parseHostPort,
  loadGiteaConfig,
} from '../scripts/gitea-pr-radar.mjs'

describe('parseEnvLocal', () => {
  it('parses KEY=VALUE lines, skipping comments and blanks', () => {
    const env = parseEnvLocal('# comment\n\nGITEA_HOST=http://x:3001\nGITEA_TOKEN=abc123\n')
    expect(env.GITEA_HOST).toBe('http://x:3001')
    expect(env.GITEA_TOKEN).toBe('abc123')
  })

  it('strips surrounding quotes and trims whitespace', () => {
    const env = parseEnvLocal('  GITEA_TOKEN = "tok"  \nGITEA_HOST=\'http://y\'')
    expect(env.GITEA_TOKEN).toBe('tok')
    expect(env.GITEA_HOST).toBe('http://y')
  })

  it('returns an empty object for empty input', () => {
    expect(parseEnvLocal('')).toEqual({})
  })
})

describe('detectPrConflicts', () => {
  const prs = [
    { number: 1, title: 'A', changedFiles: ['src/a.ts', 'src/b.ts'] },
    { number: 2, title: 'B', changedFiles: ['docs/x.md'] },
  ]

  it('returns PRs whose changed files overlap the push set, with the overlap list', () => {
    const conflicts = detectPrConflicts(['src/b.ts', 'src/c.ts'], prs)
    expect(conflicts).toEqual([{ number: 1, title: 'A', overlap: ['src/b.ts'] }])
  })

  it('returns empty when there is no overlap', () => {
    expect(detectPrConflicts(['src/z.ts'], prs)).toEqual([])
  })

  it('tolerates PRs missing changedFiles (fail-open, no throw)', () => {
    expect(detectPrConflicts(['src/a.ts'], [{ number: 3, title: 'C' }])).toEqual([])
  })
})

describe('parseHostPort', () => {
  it('splits host and port from an http url', () => {
    expect(parseHostPort('http://product-demo.tvustream.com:3001')).toEqual({
      host: 'product-demo.tvustream.com',
      port: '3001',
    })
  })

  it('defaults port to 80 for http with no explicit port', () => {
    expect(parseHostPort('http://example.org')).toEqual({ host: 'example.org', port: '80' })
  })

  it('returns null for unparseable input', () => {
    expect(parseHostPort('')).toBeNull()
  })
})

describe('loadGiteaConfig token resolution', () => {
  const NO_DIR = '/tmp/tvu-no-such-dir-xyz' // forces the .env.local read to no-op

  it('falls back to the git credential reader when no env/file token is present', () => {
    const cfg = loadGiteaConfig(NO_DIR, {
      env: { GITEA_HOST: 'http://h:3001' },
      credentialReader: () => 'kc-token',
    })
    expect(cfg).not.toBeNull()
    expect(cfg.token).toBe('kc-token')
    expect(cfg.host).toBe('http://h:3001')
  })

  it('prefers an explicit env token over the credential fallback', () => {
    const cfg = loadGiteaConfig(NO_DIR, {
      env: { GITEA_HOST: 'http://h:3001', GITEA_TOKEN: 'explicit' },
      credentialReader: () => 'kc-token',
    })
    expect(cfg.token).toBe('explicit')
  })

  it('returns null when neither env/file token nor credential reader yields a token', () => {
    const cfg = loadGiteaConfig(NO_DIR, {
      env: { GITEA_HOST: 'http://h:3001' },
      credentialReader: () => null,
    })
    expect(cfg).toBeNull()
  })
})

describe('formatPrSummary', () => {
  it('reports zero cleanly', () => {
    expect(formatPrSummary([])).toEqual(['当前 0 个 open PR。'])
  })

  it('lists each PR with number, branch flow, title and author', () => {
    const lines = formatPrSummary([
      { number: 7, title: 'Fix', head: { ref: 'feat/x' }, base: { ref: 'master' }, user: { login: 'nz' } },
    ])
    expect(lines[0]).toContain('1 个 open PR')
    expect(lines[1]).toContain('#7')
    expect(lines[1]).toContain('feat/x → master')
    expect(lines[1]).toContain('Fix')
    expect(lines[1]).toContain('nz')
  })
})
