import type { Prisma, SprintStatus } from '@prisma/client'
import { prisma } from '@/lib/prisma'
import {
  mergeSettings,
  parseUserSettings,
  type UserSettings,
} from '@/lib/user-settings'

export type ActiveSprint = {
  id: string
  code: string
  status: SprintStatus
}

async function readSettings(userId: string): Promise<UserSettings> {
  const user = await prisma.user.findUnique({
    where: { id: userId },
    select: { settings: true },
  })
  return parseUserSettings(user?.settings)
}

async function writeSettings(userId: string, next: UserSettings): Promise<void> {
  await prisma.user.update({
    where: { id: userId },
    data: { settings: next as unknown as Prisma.InputJsonValue },
  })
}

async function notifyUserSettings(
  userId: string,
  patch: Partial<UserSettings>,
): Promise<void> {
  await prisma.$executeRaw`
    SELECT pg_notify('scrum4me_changes', ${JSON.stringify({
      kind: 'user_settings',
      userId,
      patch,
    })}::text)
  `
}

type StoredActiveSprintState =
  | { kind: 'unset' }
  | { kind: 'cleared' }
  | { kind: 'set'; sprintId: string }

export function readStoredActiveSprintState(
  settings: UserSettings,
  productId: string,
): StoredActiveSprintState {
  const map = settings.layout?.activeSprints
  if (!map || !(productId in map)) return { kind: 'unset' }
  const value = map[productId]
  if (value === null) return { kind: 'cleared' }
  return { kind: 'set', sprintId: value }
}

export async function setActiveSprintInSettings(
  userId: string,
  productId: string,
  sprintId: string,
): Promise<void> {
  const current = await readSettings(userId)
  const patch: Partial<UserSettings> = {
    layout: {
      activeSprints: {
        ...(current.layout?.activeSprints ?? {}),
        [productId]: sprintId,
      },
    },
  }
  await writeSettings(userId, mergeSettings(current, patch))
  await notifyUserSettings(userId, patch)
}

export async function clearActiveSprintInSettings(
  userId: string,
  productId: string,
): Promise<void> {
  const current = await readSettings(userId)
  const nextActiveSprints: Record<string, string | null> = {
    ...(current.layout?.activeSprints ?? {}),
    [productId]: null,
  }
  const next: UserSettings = {
    ...current,
    layout: { ...current.layout, activeSprints: nextActiveSprints },
  }
  await writeSettings(userId, next)
  await notifyUserSettings(userId, {
    layout: { activeSprints: nextActiveSprints },
  })
}

/**
 * PBI-79: persisteer sprint-keuze + bijbehorende PBI/story-selectie atomair.
 * Sprintkeuze blijft 'sleutel met null = bewust geen sprint'-contract trouw;
 * activePbi/activeStory volgen dezelfde semantiek (null = expliciet leeg).
 */
export async function setActiveSelectionInSettings(
  userId: string,
  productId: string,
  selection: {
    sprintId: string | null
    pbiId?: string | null
    storyId?: string | null
  },
): Promise<void> {
  const current = await readSettings(userId)
  const nextActiveSprints: Record<string, string | null> = {
    ...(current.layout?.activeSprints ?? {}),
    [productId]: selection.sprintId,
  }
  const nextActivePbis: Record<string, string | null> = {
    ...(current.layout?.activePbis ?? {}),
  }
  if (selection.pbiId !== undefined) {
    nextActivePbis[productId] = selection.pbiId
  }
  const nextActiveStories: Record<string, string | null> = {
    ...(current.layout?.activeStories ?? {}),
  }
  if (selection.storyId !== undefined) {
    nextActiveStories[productId] = selection.storyId
  }

  const next: UserSettings = {
    ...current,
    layout: {
      ...current.layout,
      activeSprints: nextActiveSprints,
      activePbis: nextActivePbis,
      activeStories: nextActiveStories,
    },
  }
  await writeSettings(userId, next)
  await notifyUserSettings(userId, {
    layout: {
      activeSprints: nextActiveSprints,
      activePbis: nextActivePbis,
      activeStories: nextActiveStories,
    },
  })
}

export async function resolveActiveSprint(
  productId: string,
  userId: string,
): Promise<ActiveSprint | null> {
  const settings = await readSettings(userId)
  const state = readStoredActiveSprintState(settings, productId)

  if (state.kind === 'cleared') return null

  if (state.kind === 'set') {
    const sprint = await prisma.sprint.findFirst({
      where: { id: state.sprintId, product_id: productId },
      select: { id: true, code: true, status: true },
    })
    if (sprint) return sprint
  }

  const open = await prisma.sprint.findFirst({
    where: { product_id: productId, status: 'OPEN' },
    orderBy: { created_at: 'desc' },
    select: { id: true, code: true, status: true },
  })
  if (open) return open

  const closed = await prisma.sprint.findFirst({
    where: { product_id: productId, status: 'CLOSED' },
    orderBy: { created_at: 'desc' },
    select: { id: true, code: true, status: true },
  })
  return closed ?? null
}
