'use server'

// ST-1005: Server Actions voor de mobiele zijde van de QR-pairing-flow (M10).
//
// Aangeroepen door de Client Component op /m/pair zodra die het #id=…&s=…
// fragment uit de URL heeft geparsed. De mobiele gebruiker is hier al
// geauthenticeerd via de bestaande iron-session (de page zit achter de
// (app)/layout.tsx-guard).
//
// Volgt docs/patterns/server-action.md: getSession + Zod + demo-guard
// (uitsluitend op approvePairing — read-only en cancel mag iedereen).

import { z } from 'zod'
import { prisma } from '@/lib/prisma'
import { getSession } from '@/lib/auth'
import { verifyToken } from '@/lib/auth/pairing'

const APPROVED_TTL_MS = 5 * 60 * 1000

const inputSchema = z.object({
  pairingId: z.string().cuid(),
  mobileSecret: z.string().min(40), // 32 bytes base64url ≈ 43 chars
})

type ActionFail = { ok: false; error: string }
type ApprovalView = {
  ok: true
  desktop_ua: string | null
  desktop_ip: string | null
  username: string
}

type PendingPairing = {
  status: string
  expires_at: Date
  secret_hash: string
  desktop_ua: string | null
  desktop_ip: string | null
}

type LoadResult =
  | { kind: 'error'; error: string }
  | { kind: 'ok'; pairing: PendingPairing }

async function loadPendingPairing(
  pairingId: string,
  mobileSecret: string,
): Promise<LoadResult> {
  const pairing = await prisma.loginPairing.findUnique({
    where: { id: pairingId },
    select: {
      status: true,
      expires_at: true,
      secret_hash: true,
      desktop_ua: true,
      desktop_ip: true,
    },
  })
  if (!pairing) return { kind: 'error', error: 'Pairing niet gevonden' }
  if (pairing.expires_at < new Date()) return { kind: 'error', error: 'Pairing verlopen' }
  if (pairing.status !== 'pending') return { kind: 'error', error: 'Pairing al afgehandeld' }
  if (!verifyToken(mobileSecret, pairing.secret_hash)) {
    return { kind: 'error', error: 'Ongeldig pairing-geheim' }
  }
  return { kind: 'ok', pairing }
}

export async function getPairingForApproval(
  pairingId: string,
  mobileSecret: string,
): Promise<ApprovalView | ActionFail> {
  const session = await getSession()
  if (!session.userId) return { ok: false, error: 'Niet ingelogd' }

  const parsed = inputSchema.safeParse({ pairingId, mobileSecret })
  if (!parsed.success) return { ok: false, error: 'Ongeldige invoer' }

  const result = await loadPendingPairing(parsed.data.pairingId, parsed.data.mobileSecret)
  if (result.kind === 'error') return { ok: false, error: result.error }

  const me = await prisma.user.findUnique({
    where: { id: session.userId },
    select: { username: true },
  })
  return {
    ok: true,
    desktop_ua: result.pairing.desktop_ua,
    desktop_ip: result.pairing.desktop_ip,
    username: me?.username ?? '',
  }
}

export async function approvePairing(
  pairingId: string,
  mobileSecret: string,
): Promise<{ ok: true } | ActionFail> {
  const session = await getSession()
  if (!session.userId) return { ok: false, error: 'Niet ingelogd' }
  if (session.isDemo) return { ok: false, error: 'Niet beschikbaar in demo-modus' }

  const parsed = inputSchema.safeParse({ pairingId, mobileSecret })
  if (!parsed.success) return { ok: false, error: 'Ongeldige invoer' }

  const result = await loadPendingPairing(parsed.data.pairingId, parsed.data.mobileSecret)
  if (result.kind === 'error') return { ok: false, error: result.error }

  await prisma.loginPairing.update({
    where: { id: parsed.data.pairingId },
    data: {
      status: 'approved',
      user_id: session.userId,
      approved_at: new Date(),
      expires_at: new Date(Date.now() + APPROVED_TTL_MS),
    },
  })
  // Postgres-trigger emit pg_notify('scrum4me_pairing', …) automatisch — de
  // desktop-SSE in ST-1004 vangt het op. Geen revalidatePath nodig: deze page
  // heeft geen server-state om te ververversen, en de mobiele tab gaat naar
  // de "klaar"-state direct in de Client Component.
  return { ok: true }
}

export async function cancelPairing(
  pairingId: string,
  mobileSecret: string,
): Promise<{ ok: true } | ActionFail> {
  const session = await getSession()
  if (!session.userId) return { ok: false, error: 'Niet ingelogd' }
  // Cancel is een DB-write — onder de demo-write-block-regel.
  if (session.isDemo) return { ok: false, error: 'Niet beschikbaar in demo-modus' }

  const parsed = inputSchema.safeParse({ pairingId, mobileSecret })
  if (!parsed.success) return { ok: false, error: 'Ongeldige invoer' }

  const result = await loadPendingPairing(parsed.data.pairingId, parsed.data.mobileSecret)
  if (result.kind === 'error') return { ok: false, error: result.error }

  await prisma.loginPairing.update({
    where: { id: parsed.data.pairingId },
    data: { status: 'cancelled' },
  })
  return { ok: true }
}
