export interface BranchListItem {
  name: string
  isLocal: boolean
  isRemote: boolean
  isDefault: boolean
  lastCommitDate?: string
  isCheckedOut?: boolean
}

interface PRUrlParts {
  owner: string
  repo: string
  number: number
}

export interface PRInfo {
  headRefName: string
  headRepositoryOwner?: { login: string }
  isCrossRepository: boolean
  title: string
}

interface WorktreeEntry {
  path: string
  branch: string
  bare: boolean
  detached: boolean
}

type PRErrorKind = "not_found" | "gh_missing" | "gh_auth" | "unknown"

export type WorktreeSetupErrorCode = "git_not_found" | "not_git_repo" | "lfs_missing" | "no_commits"

export function parsePRUrl(url: string): PRUrlParts | null {
  let normalized = url.trim()
  if (!normalized.startsWith("http")) normalized = `https://${normalized}`
  normalized = normalized.replace(/\/+$/, "")
  const match = normalized.match(/\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/)
  if (!match) return null
  return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) }
}

export function localBranchName(info: PRInfo): string {
  if (info.isCrossRepository) {
    const owner = info.headRepositoryOwner?.login?.toLowerCase()
    if (owner) return `${owner}/${info.headRefName}`
  }
  return info.headRefName
}

export function parseForEachRefOutput(raw: string): {
  locals: Set<string>
  remotes: Set<string>
  dates: Map<string, string>
} {
  const locals = new Set<string>()
  const remotes = new Set<string>()
  const dates = new Map<string, string>()

  for (const line of raw.split("\n")) {
    if (!line) continue
    const [ref, date] = line.split("\t")
    if (ref.includes("HEAD")) continue

    if (ref.startsWith("refs/heads/")) {
      const name = ref.slice(11)
      locals.add(name)
      if (date && !dates.has(name)) dates.set(name, date)
    } else if (ref.startsWith("refs/remotes/origin/")) {
      const name = ref.slice(20)
      remotes.add(name)
      if (date && !dates.has(name)) dates.set(name, date)
    }
  }

  return { locals, remotes, dates }
}

export function buildBranchList(
  locals: Set<string>,
  remotes: Set<string>,
  dates: Map<string, string>,
  defaultBranch: string,
): BranchListItem[] {
  const all = new Set([...locals, ...remotes])
  const branches: BranchListItem[] = [...all].map((name) => ({
    name,
    isLocal: locals.has(name),
    isRemote: remotes.has(name),
    isDefault: name === defaultBranch,
    lastCommitDate: dates.get(name),
  }))

  branches.sort((a, b) => {
    if (a.isDefault && !b.isDefault) return -1
    if (!a.isDefault && b.isDefault) return 1
    if (a.lastCommitDate && b.lastCommitDate) return b.lastCommitDate.localeCompare(a.lastCommitDate)
    return 0
  })

  return branches
}

export function parseWorktreeList(raw: string): WorktreeEntry[] {
  const entries: WorktreeEntry[] = []
  for (const block of raw.split("\n\n")) {
    if (!block.trim()) continue
    const lines = block.split("\n")
    const wtPath = lines.find((l) => l.startsWith("worktree "))?.slice(9)
    if (!wtPath) continue

    const branchLine = lines.find((l) => l.startsWith("branch "))
    const bare = lines.some((l) => l === "bare")
    const detached = lines.some((l) => l === "detached")
    const branch = branchLine ? branchLine.slice(7).replace("refs/heads/", "") : detached ? "(detached)" : "unknown"

    entries.push({ path: wtPath, branch, bare, detached })
  }
  return entries
}

export function checkedOutBranchesFromWorktreeList(raw: string): Set<string> {
  const result = new Set<string>()
  for (const entry of parseWorktreeList(raw)) {
    if (!entry.bare && !entry.detached) result.add(entry.branch)
  }
  return result
}

const SAFE_GIT_REF = /^[a-zA-Z0-9._\-/]+$/

export function validateGitRef(value: string, label: string): void {
  if (!value || !SAFE_GIT_REF.test(value) || value.startsWith("-") || value.includes("..")) {
    throw new Error(`Unsafe ${label}: "${value}"`)
  }
}

/**
 * Normalize a filesystem path for cross-platform comparison.
 * Converts backslashes to forward slashes, strips trailing slashes,
 * and lowercases Windows drive-letter paths (case-insensitive filesystem).
 */
export function normalizePath(p: string): string {
  const normalized = p.replace(/\\/g, "/").replace(/\/+$/, "")
  if (/^[A-Za-z]:/.test(normalized)) return normalized.toLowerCase()
  return normalized
}

export function classifyPRError(msg: string): PRErrorKind {
  if (msg.includes("command not found") || msg.includes("ENOENT") || msg.includes("is not recognized"))
    return "gh_missing"
  if (msg.includes("not logged") || msg.includes("auth login")) return "gh_auth"
  if (msg.includes("not found") || msg.includes("Could not resolve")) return "not_found"
  return "unknown"
}

export function classifyWorktreeError(msg: string): WorktreeSetupErrorCode | undefined {
  if (msg.includes("ENOENT") || msg.includes("not found in PATH")) return "git_not_found"
  if (msg.includes("not a git repository")) return "not_git_repo"
  if (msg.includes("Git LFS") && msg.includes("not found")) return "lfs_missing"
  if (msg.includes("no commits yet")) return "no_commits"
  return undefined
}
