/**
 * Live Control Service
 *
 * Manages real-time control of live final events within a round.
 * Handles session management, project cursor navigation, queue reordering,
 * pause/resume, and cohort voting windows.
 *
 * The LiveProgressCursor tracks the current position in a live presentation
 * sequence. Cohorts group projects for voting with configurable windows.
 */

import type { PrismaClient } from '@prisma/client'
import { logAudit } from '@/server/utils/audit'

// ─── Types ──────────────────────────────────────────────────────────────────

export interface SessionResult {
  success: boolean
  sessionId: string | null
  cursorId: string | null
  errors?: string[]
}

export interface CursorState {
  roundId: string
  sessionId: string
  activeProjectId: string | null
  activeOrderIndex: number
  isPaused: boolean
  totalProjects: number
}

// ─── Helpers ────────────────────────────────────────────────────────────────

function generateSessionId(): string {
  const timestamp = Date.now().toString(36)
  const random = Math.random().toString(36).substring(2, 8)
  return `live-${timestamp}-${random}`
}

// ─── Start Session ──────────────────────────────────────────────────────────

/**
 * Create or reset a LiveProgressCursor for a round. If a cursor already exists,
 * it is reset to the beginning. A new sessionId is always generated.
 */
export async function startSession(
  roundId: string,
  actorId: string,
  prisma: PrismaClient | any
): Promise<SessionResult> {
  try {
    // Verify round exists and is a LIVE_FINAL type
    const round = await prisma.round.findUnique({
      where: { id: roundId },
    })

    if (!round) {
      return {
        success: false,
        sessionId: null,
        cursorId: null,
        errors: [`Round ${roundId} not found`],
      }
    }

    if (round.roundType !== 'LIVE_FINAL') {
      return {
        success: false,
        sessionId: null,
        cursorId: null,
        errors: [
          `Round "${round.name}" is type ${round.roundType}, expected LIVE_FINAL`,
        ],
      }
    }

    // Find the first project in the first cohort
    const firstCohortProject = await prisma.cohortProject.findFirst({
      where: {
        cohort: { roundId },
      },
      orderBy: { sortOrder: 'asc' as const },
      select: { projectId: true },
    })

    const sessionId = generateSessionId()

    // Upsert the cursor (one per round)
    const cursor = await prisma.liveProgressCursor.upsert({
      where: { roundId },
      create: {
        roundId,
        sessionId,
        activeProjectId: firstCohortProject?.projectId ?? null,
        activeOrderIndex: 0,
        isPaused: false,
      },
      update: {
        sessionId,
        activeProjectId: firstCohortProject?.projectId ?? null,
        activeOrderIndex: 0,
        isPaused: false,
      },
    })

    // Decision audit log
    await prisma.decisionAuditLog.create({
      data: {
        eventType: 'live.session_started',
        entityType: 'LiveProgressCursor',
        entityId: cursor.id,
        actorId,
        detailsJson: {
          roundId,
          sessionId,
          firstProjectId: firstCohortProject?.projectId ?? null,
        },
      },
    })

    await logAudit({
      prisma,
      userId: actorId,
      action: 'LIVE_SESSION_STARTED',
      entityType: 'LiveProgressCursor',
      entityId: cursor.id,
      detailsJson: { roundId, sessionId },
    })

    return {
      success: true,
      sessionId,
      cursorId: cursor.id,
    }
  } catch (error) {
    console.error('[LiveControl] Failed to start session:', error)
    return {
      success: false,
      sessionId: null,
      cursorId: null,
      errors: [
        error instanceof Error ? error.message : 'Failed to start live session',
      ],
    }
  }
}

// ─── Set Active Project ─────────────────────────────────────────────────────

/**
 * Set the currently active project in the live session.
 * Validates that the project belongs to a cohort in this round and performs
 * a version check on the cursor's sessionId to prevent stale updates.
 */
export async function setActiveProject(
  roundId: string,
  projectId: string,
  actorId: string,
  prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
  try {
    // Verify cursor exists
    const cursor = await prisma.liveProgressCursor.findUnique({
      where: { roundId },
    })

    if (!cursor) {
      return {
        success: false,
        errors: ['No live session found for this round. Start a session first.'],
      }
    }

    // Verify project is in a cohort for this round
    const cohortProject = await prisma.cohortProject.findFirst({
      where: {
        projectId,
        cohort: { roundId },
      },
      select: { id: true, sortOrder: true },
    })

    if (!cohortProject) {
      return {
        success: false,
        errors: [
          `Project ${projectId} is not in any cohort for round ${roundId}`,
        ],
      }
    }

    // Update cursor
    await prisma.liveProgressCursor.update({
      where: { roundId },
      data: {
        activeProjectId: projectId,
        activeOrderIndex: cohortProject.sortOrder,
      },
    })

    // Audit
    await prisma.decisionAuditLog.create({
      data: {
        eventType: 'live.cursor_updated',
        entityType: 'LiveProgressCursor',
        entityId: cursor.id,
        actorId,
        detailsJson: {
          roundId,
          projectId,
          orderIndex: cohortProject.sortOrder,
          action: 'setActiveProject',
        },
      },
    })

    await logAudit({
      prisma,
      userId: actorId,
      action: 'LIVE_SET_ACTIVE_PROJECT',
      entityType: 'LiveProgressCursor',
      entityId: cursor.id,
      detailsJson: { projectId, orderIndex: cohortProject.sortOrder },
    })

    return { success: true }
  } catch (error) {
    console.error('[LiveControl] Failed to set active project:', error)
    return {
      success: false,
      errors: [
        error instanceof Error
          ? error.message
          : 'Failed to set active project',
      ],
    }
  }
}

// ─── Jump to Project ────────────────────────────────────────────────────────

/**
 * Jump to a project by its order index in the cohort queue.
 */
export async function jumpToProject(
  roundId: string,
  orderIndex: number,
  actorId: string,
  prisma: PrismaClient | any
): Promise<{ success: boolean; projectId?: string; errors?: string[] }> {
  try {
    const cursor = await prisma.liveProgressCursor.findUnique({
      where: { roundId },
    })

    if (!cursor) {
      return {
        success: false,
        errors: ['No live session found for this round'],
      }
    }

    // Find the CohortProject at the given sort order
    const cohortProject = await prisma.cohortProject.findFirst({
      where: {
        cohort: { roundId },
        sortOrder: orderIndex,
      },
      select: { projectId: true, sortOrder: true },
    })

    if (!cohortProject) {
      return {
        success: false,
        errors: [`No project found at order index ${orderIndex}`],
      }
    }

    // Update cursor
    await prisma.liveProgressCursor.update({
      where: { roundId },
      data: {
        activeProjectId: cohortProject.projectId,
        activeOrderIndex: orderIndex,
      },
    })

    await prisma.decisionAuditLog.create({
      data: {
        eventType: 'live.cursor_updated',
        entityType: 'LiveProgressCursor',
        entityId: cursor.id,
        actorId,
        detailsJson: {
          roundId,
          projectId: cohortProject.projectId,
          orderIndex,
          action: 'jumpToProject',
        },
      },
    })

    await logAudit({
      prisma,
      userId: actorId,
      action: 'LIVE_JUMP_TO_PROJECT',
      entityType: 'LiveProgressCursor',
      entityId: cursor.id,
      detailsJson: { orderIndex, projectId: cohortProject.projectId },
    })

    return { success: true, projectId: cohortProject.projectId }
  } catch (error) {
    console.error('[LiveControl] Failed to jump to project:', error)
    return {
      success: false,
      errors: [
        error instanceof Error ? error.message : 'Failed to jump to project',
      ],
    }
  }
}

// ─── Reorder Queue ──────────────────────────────────────────────────────────

/**
 * Reorder the presentation queue by updating CohortProject sortOrder values.
 * newOrder is an array of cohortProjectIds in the desired order.
 */
export async function reorderQueue(
  roundId: string,
  newOrder: string[],
  actorId: string,
  prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
  try {
    // Verify all provided IDs belong to cohorts in this round
    const cohortProjects = await prisma.cohortProject.findMany({
      where: {
        id: { in: newOrder },
        cohort: { roundId },
      },
      select: { id: true },
    })

    const validIds = new Set(cohortProjects.map((cp: any) => cp.id))
    const invalidIds = newOrder.filter((id) => !validIds.has(id))

    if (invalidIds.length > 0) {
      return {
        success: false,
        errors: [
          `CohortProject IDs not found in round ${roundId}: ${invalidIds.join(', ')}`,
        ],
      }
    }

    // Update sortOrder for each item
    await prisma.$transaction(
      newOrder.map((cohortProjectId, index) =>
        prisma.cohortProject.update({
          where: { id: cohortProjectId },
          data: { sortOrder: index },
        })
      )
    )

    const cursor = await prisma.liveProgressCursor.findUnique({
      where: { roundId },
    })

    if (cursor) {
      await prisma.decisionAuditLog.create({
        data: {
          eventType: 'live.queue_reordered',
          entityType: 'LiveProgressCursor',
          entityId: cursor.id,
          actorId,
          detailsJson: {
            roundId,
            newOrderCount: newOrder.length,
          },
        },
      })
    }

    await logAudit({
      prisma,
      userId: actorId,
      action: 'LIVE_REORDER_QUEUE',
      entityType: 'Round',
      entityId: roundId,
      detailsJson: { reorderedCount: newOrder.length },
    })

    return { success: true }
  } catch (error) {
    console.error('[LiveControl] Failed to reorder queue:', error)
    return {
      success: false,
      errors: [
        error instanceof Error ? error.message : 'Failed to reorder queue',
      ],
    }
  }
}

// ─── Pause / Resume ─────────────────────────────────────────────────────────

/**
 * Toggle the pause state of a live session.
 */
export async function pauseResume(
  roundId: string,
  isPaused: boolean,
  actorId: string,
  prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
  try {
    const cursor = await prisma.liveProgressCursor.findUnique({
      where: { roundId },
    })

    if (!cursor) {
      return {
        success: false,
        errors: ['No live session found for this round'],
      }
    }

    await prisma.liveProgressCursor.update({
      where: { roundId },
      data: { isPaused },
    })

    await prisma.decisionAuditLog.create({
      data: {
        eventType: isPaused ? 'live.session_paused' : 'live.session_resumed',
        entityType: 'LiveProgressCursor',
        entityId: cursor.id,
        actorId,
        detailsJson: {
          roundId,
          isPaused,
          sessionId: cursor.sessionId,
        },
      },
    })

    await logAudit({
      prisma,
      userId: actorId,
      action: isPaused ? 'LIVE_SESSION_PAUSED' : 'LIVE_SESSION_RESUMED',
      entityType: 'LiveProgressCursor',
      entityId: cursor.id,
      detailsJson: { roundId, isPaused },
    })

    return { success: true }
  } catch (error) {
    console.error('[LiveControl] Failed to pause/resume:', error)
    return {
      success: false,
      errors: [
        error instanceof Error
          ? error.message
          : 'Failed to toggle pause state',
      ],
    }
  }
}

// ─── Cohort Window Management ───────────────────────────────────────────────

/**
 * Open a cohort's voting window. Sets isOpen to true and records the
 * window open timestamp.
 */
export async function openCohortWindow(
  cohortId: string,
  actorId: string,
  prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
  try {
    const cohort = await prisma.cohort.findUnique({
      where: { id: cohortId },
    })

    if (!cohort) {
      return { success: false, errors: [`Cohort ${cohortId} not found`] }
    }

    if (cohort.isOpen) {
      return {
        success: false,
        errors: [`Cohort "${cohort.name}" is already open`],
      }
    }

    const now = new Date()

    await prisma.cohort.update({
      where: { id: cohortId },
      data: {
        isOpen: true,
        windowOpenAt: now,
      },
    })

    await prisma.decisionAuditLog.create({
      data: {
        eventType: 'live.cohort_opened',
        entityType: 'Cohort',
        entityId: cohortId,
        actorId,
        detailsJson: {
          cohortName: cohort.name,
          roundId: cohort.roundId,
          openedAt: now.toISOString(),
        },
      },
    })

    await logAudit({
      prisma,
      userId: actorId,
      action: 'LIVE_COHORT_OPENED',
      entityType: 'Cohort',
      entityId: cohortId,
      detailsJson: { cohortName: cohort.name, roundId: cohort.roundId },
    })

    return { success: true }
  } catch (error) {
    console.error('[LiveControl] Failed to open cohort window:', error)
    return {
      success: false,
      errors: [
        error instanceof Error
          ? error.message
          : 'Failed to open cohort window',
      ],
    }
  }
}

/**
 * Close a cohort's voting window. Sets isOpen to false and records the
 * window close timestamp.
 */
export async function closeCohortWindow(
  cohortId: string,
  actorId: string,
  prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
  try {
    const cohort = await prisma.cohort.findUnique({
      where: { id: cohortId },
    })

    if (!cohort) {
      return { success: false, errors: [`Cohort ${cohortId} not found`] }
    }

    if (!cohort.isOpen) {
      return {
        success: false,
        errors: [`Cohort "${cohort.name}" is already closed`],
      }
    }

    const now = new Date()

    await prisma.cohort.update({
      where: { id: cohortId },
      data: {
        isOpen: false,
        windowCloseAt: now,
      },
    })

    await prisma.decisionAuditLog.create({
      data: {
        eventType: 'live.cohort_closed',
        entityType: 'Cohort',
        entityId: cohortId,
        actorId,
        detailsJson: {
          cohortName: cohort.name,
          roundId: cohort.roundId,
          closedAt: now.toISOString(),
        },
      },
    })

    await logAudit({
      prisma,
      userId: actorId,
      action: 'LIVE_COHORT_CLOSED',
      entityType: 'Cohort',
      entityId: cohortId,
      detailsJson: { cohortName: cohort.name, roundId: cohort.roundId },
    })

    return { success: true }
  } catch (error) {
    console.error('[LiveControl] Failed to close cohort window:', error)
    return {
      success: false,
      errors: [
        error instanceof Error
          ? error.message
          : 'Failed to close cohort window',
      ],
    }
  }
}
