import { Prisma } from '@prisma/client'
import { prisma } from '@/lib/prisma'

const MAX_AUTO_CODE_ATTEMPTS = 3

export function isCodeUniqueConflict(error: unknown): boolean {
  if (!(error instanceof Prisma.PrismaClientKnownRequestError)) return false
  if (error.code !== 'P2002') return false
  const target = (error.meta as { target?: string[] | string } | undefined)?.target
  if (!target) return false
  if (Array.isArray(target)) return target.includes('code')
  return target.includes('code')
}

/**
 * Generate an auto code, then run the create. If the insert collides with an
 * existing code (P2002 on the code column), regenerate and retry up to a small
 * number of attempts. Protects against the SELECT-MAX → INSERT race when two
 * concurrent requests pick the same next number.
 */
export async function createWithCodeRetry<T>(
  generate: () => Promise<string>,
  create: (code: string) => Promise<T>,
): Promise<T> {
  let lastError: unknown
  for (let attempt = 0; attempt < MAX_AUTO_CODE_ATTEMPTS; attempt++) {
    const code = await generate()
    try {
      return await create(code)
    } catch (e) {
      if (isCodeUniqueConflict(e)) {
        lastError = e
        continue
      }
      throw e
    }
  }
  throw lastError ?? new Error('Kon geen unieke code genereren')
}

const STORY_AUTO_RE = /^ST-(\d+)$/
const PBI_AUTO_RE = /^PBI-(\d+)$/
const TASK_AUTO_RE = /^T-(\d+)$/
const SPRINT_AUTO_RE = /^SP-(\d+)$/

function nextSequential(existing: (string | null)[], pattern: RegExp): number {
  let max = 0
  for (const c of existing) {
    if (!c) continue
    const m = c.match(pattern)
    if (m) {
      const n = Number.parseInt(m[1], 10)
      if (!Number.isNaN(n) && n > max) max = n
    }
  }
  return max + 1
}

export async function generateNextStoryCode(productId: string): Promise<string> {
  const stories = await prisma.story.findMany({
    where: { product_id: productId },
    select: { code: true },
  })
  const next = nextSequential(stories.map((s) => s.code), STORY_AUTO_RE)
  return `ST-${String(next).padStart(3, '0')}`
}

export async function generateNextPbiCode(productId: string): Promise<string> {
  const pbis = await prisma.pbi.findMany({
    where: { product_id: productId },
    select: { code: true },
  })
  const next = nextSequential(pbis.map((p) => p.code), PBI_AUTO_RE)
  return `PBI-${next}`
}

export async function generateNextTaskCode(productId: string): Promise<string> {
  const tasks = await prisma.task.findMany({
    where: { product_id: productId },
    select: { code: true },
  })
  const next = nextSequential(tasks.map((t) => t.code), TASK_AUTO_RE)
  return `T-${next}`
}

export async function generateNextSprintCode(productId: string): Promise<string> {
  const sprints = await prisma.sprint.findMany({
    where: { product_id: productId },
    select: { code: true },
  })
  const next = nextSequential(sprints.map((s) => s.code), SPRINT_AUTO_RE)
  return `SP-${next}`
}

