'use server'

import { revalidatePath } from 'next/cache'
import { prisma } from '@/lib/prisma'
import { getSession } from '@/lib/auth'
import { productAccessFilter } from '@/lib/product-access'
import { ACTIVE_JOB_STATUSES, jobStatusToApi } from '@/lib/job-status'
import { enforceUserRateLimit } from '@/lib/rate-limit'

type EnqueueResult =
  | { success: true; jobId: string }
  | { error: string; jobId?: string }

type EnqueueAllResult =
  | { success: true; count: number }
  | { error: string }

type CancelResult = { success: true } | { error: string }

export type PreviewTask = {
  id: string
  title: string
  status: string
  story_title: string
  pbi_id: string
  pbi_status: string
}

type PreflightResult =
  | { error: string }
  | { tasks: PreviewTask[]; blockerIndex: number | null; blockerReason: 'task-review' | 'pbi-blocked' | null }

export async function enqueueClaudeJobAction(taskId: string): Promise<EnqueueResult> {
  const session = await getSession()
  if (!session.userId) return { error: 'Niet ingelogd' }
  if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' }

  const limited = enforceUserRateLimit('enqueue-job', session.userId)
  if (limited) return { error: limited.error }

  if (!taskId) return { error: 'task_id is verplicht' }

  // Resolve task + product access in one query
  const task = await prisma.task.findFirst({
    where: {
      id: taskId,
      story: { product: productAccessFilter(session.userId) },
    },
    select: { id: true, story: { select: { product_id: true } } },
  })
  if (!task) return { error: 'Task niet gevonden' }

  const productId = task.story.product_id

  // Idempotency: weiger als er al een actieve job voor deze task bestaat
  const existing = await prisma.claudeJob.findFirst({
    where: { task_id: taskId, status: { in: ACTIVE_JOB_STATUSES } },
    select: { id: true },
  })
  if (existing) {
    return { error: 'Er loopt al een agent voor deze task', jobId: existing.id }
  }

  const job = await prisma.claudeJob.create({
    data: { user_id: session.userId, product_id: productId, task_id: taskId, status: 'QUEUED' },
  })

  await prisma.$executeRaw`
    SELECT pg_notify('scrum4me_changes', ${JSON.stringify({
      type: 'claude_job_enqueued',
      job_id: job.id,
      task_id: taskId,
      user_id: session.userId,
      product_id: productId,
      status: 'queued',
    })}::text)
  `

  revalidatePath(`/products/${productId}/solo`)
  return { success: true, jobId: job.id }
}

export async function enqueueAllTodoJobsAction(productId: string): Promise<EnqueueAllResult> {
  const session = await getSession()
  if (!session.userId) return { error: 'Niet ingelogd' }
  if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' }

  if (!productId) return { error: 'product_id is verplicht' }

  const product = await prisma.product.findFirst({
    where: { id: productId, ...productAccessFilter(session.userId) },
    select: { id: true },
  })
  if (!product) return { error: 'Geen toegang tot dit product' }

  const userId = session.userId

  // Match het scope dat de gebruiker op het Solo Paneel ziet:
  // alleen TO_DO-taken in de actieve sprint, in stories die aan deze
  // gebruiker zijn toegewezen. Anders queue je per ongeluk taken die
  // niet in de huidige sprint zitten of aan iemand anders toebehoren.
  const sprint = await prisma.sprint.findFirst({
    where: { product_id: productId, status: 'ACTIVE' },
    select: { id: true },
  })
  if (!sprint) return { success: true, count: 0 }

  const tasks = await prisma.task.findMany({
    where: {
      status: 'TO_DO',
      story: { sprint_id: sprint.id, assignee_id: userId },
      claude_jobs: { none: { status: { in: ACTIVE_JOB_STATUSES } } },
    },
    select: { id: true },
  })

  if (tasks.length === 0) return { success: true, count: 0 }

  const created = await prisma.$transaction(
    tasks.map(t =>
      prisma.claudeJob.create({
        data: { user_id: userId, product_id: productId, task_id: t.id, status: 'QUEUED' },
        select: { id: true, task_id: true },
      })
    )
  )

  for (const job of created) {
    await prisma.$executeRaw`
      SELECT pg_notify('scrum4me_changes', ${JSON.stringify({
        type: 'claude_job_enqueued',
        job_id: job.id,
        task_id: job.task_id,
        user_id: userId,
        product_id: productId,
        status: 'queued',
      })}::text)
    `
  }

  revalidatePath(`/products/${productId}/solo`)
  return { success: true, count: created.length }
}

export async function previewEnqueueAllAction(productId: string): Promise<PreflightResult> {
  const session = await getSession()
  if (!session.userId) return { error: 'Niet ingelogd' }
  if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' }

  if (!productId) return { error: 'product_id is verplicht' }

  const product = await prisma.product.findFirst({
    where: { id: productId, ...productAccessFilter(session.userId) },
    select: { id: true },
  })
  if (!product) return { error: 'Geen toegang tot dit product' }

  const userId = session.userId

  const sprint = await prisma.sprint.findFirst({
    where: { product_id: productId, status: 'ACTIVE' },
    select: { id: true },
  })
  if (!sprint) return { tasks: [], blockerIndex: null, blockerReason: null }

  const rawTasks = await prisma.task.findMany({
    where: {
      story: { sprint_id: sprint.id, assignee_id: userId },
      claude_jobs: { none: { status: { in: ACTIVE_JOB_STATUSES } } },
    },
    select: {
      id: true,
      title: true,
      status: true,
      story: {
        select: {
          id: true,
          title: true,
          code: true,
          pbi: { select: { id: true, status: true, priority: true, sort_order: true } },
        },
      },
    },
    orderBy: [
      { story: { pbi: { priority: 'asc' } } },
      { story: { pbi: { sort_order: 'asc' } } },
      { story: { sort_order: 'asc' } },
      { priority: 'asc' },
      { sort_order: 'asc' },
    ],
  })

  let blockerIndex: number | null = null
  let blockerReason: 'task-review' | 'pbi-blocked' | null = null

  for (let i = 0; i < rawTasks.length; i++) {
    const t = rawTasks[i]
    if (t.status === 'REVIEW') {
      blockerIndex = i
      blockerReason = 'task-review'
      break
    }
    if (t.story.pbi.status === 'BLOCKED') {
      blockerIndex = i
      blockerReason = 'pbi-blocked'
      break
    }
  }

  const displayTasks = blockerIndex !== null ? rawTasks.slice(0, blockerIndex + 1) : rawTasks

  const tasks: PreviewTask[] = displayTasks.map(t => ({
    id: t.id,
    title: t.title,
    status: t.status,
    story_title: t.story.title,
    pbi_id: t.story.pbi.id,
    pbi_status: t.story.pbi.status,
  }))

  return { tasks, blockerIndex, blockerReason }
}

export async function enqueueClaudeJobsBatchAction(
  productId: string,
  taskIds: string[]
): Promise<EnqueueAllResult> {
  const session = await getSession()
  if (!session.userId) return { error: 'Niet ingelogd' }
  if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' }

  const limited = enforceUserRateLimit('enqueue-job', session.userId)
  if (limited) return { error: limited.error }

  if (!productId) return { error: 'product_id is verplicht' }
  if (!taskIds.length) return { success: true, count: 0 }

  const product = await prisma.product.findFirst({
    where: { id: productId, ...productAccessFilter(session.userId) },
    select: { id: true },
  })
  if (!product) return { error: 'Geen toegang tot dit product' }

  const userId = session.userId

  const sprint = await prisma.sprint.findFirst({
    where: { product_id: productId, status: 'ACTIVE' },
    select: { id: true },
  })
  if (!sprint) return { error: 'Geen actieve sprint gevonden' }

  const authorizedTasks = await prisma.task.findMany({
    where: {
      id: { in: taskIds },
      story: { sprint_id: sprint.id, assignee_id: userId },
    },
    select: {
      id: true,
      claude_jobs: {
        where: { status: { in: ACTIVE_JOB_STATUSES } },
        select: { id: true },
      },
    },
  })

  if (authorizedTasks.length !== taskIds.length) {
    return { error: 'Een of meer taken zijn niet toegankelijk voor deze gebruiker' }
  }

  const queueable = authorizedTasks.filter(t => t.claude_jobs.length === 0)
  if (queueable.length === 0) return { success: true, count: 0 }

  const queueableIds = new Set(queueable.map(t => t.id))
  const orderedQueueable = taskIds.filter(id => queueableIds.has(id))

  const created = await prisma.$transaction(
    orderedQueueable.map(taskId =>
      prisma.claudeJob.create({
        data: { user_id: userId, product_id: productId, task_id: taskId, status: 'QUEUED' },
        select: { id: true, task_id: true },
      })
    )
  )

  for (const job of created) {
    await prisma.$executeRaw`
      SELECT pg_notify('scrum4me_changes', ${JSON.stringify({
        type: 'claude_job_enqueued',
        job_id: job.id,
        task_id: job.task_id,
        user_id: userId,
        product_id: productId,
        status: 'queued',
      })}::text)
    `
  }

  revalidatePath(`/products/${productId}/solo`)
  return { success: true, count: created.length }
}

export async function cancelClaudeJobAction(jobId: string): Promise<CancelResult> {
  const session = await getSession()
  if (!session.userId) return { error: 'Niet ingelogd' }
  if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' }

  if (!jobId) return { error: 'job_id is verplicht' }

  const job = await prisma.claudeJob.findFirst({
    where: { id: jobId, user_id: session.userId },
    select: { id: true, status: true, task_id: true, product_id: true },
  })
  if (!job) return { error: 'Job niet gevonden' }

  if (!ACTIVE_JOB_STATUSES.includes(job.status)) {
    return { error: 'Alleen actieve jobs kunnen geannuleerd worden' }
  }

  await prisma.claudeJob.update({
    where: { id: jobId },
    data: { status: 'CANCELLED', finished_at: new Date() },
  })

  await prisma.$executeRaw`
    SELECT pg_notify('scrum4me_changes', ${JSON.stringify({
      type: 'claude_job_status',
      job_id: jobId,
      task_id: job.task_id,
      user_id: session.userId,
      product_id: job.product_id,
      status: jobStatusToApi('CANCELLED'),
    })}::text)
  `

  revalidatePath(`/products/${job.product_id}/solo`)
  return { success: true }
}
