/**
 * Submission Round Manager Service
 *
 * Manages SubmissionWindow lifecycle, file requirement enforcement,
 * and deadline policies.
 */

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

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

export type WindowLifecycleResult = {
  success: boolean
  errors?: string[]
}

export type DeadlineStatus = {
  status: 'OPEN' | 'GRACE' | 'CLOSED' | 'LOCKED'
  graceExpiresAt?: Date
  deadlinePolicy: DeadlinePolicy
}

export type SubmissionValidationResult = {
  valid: boolean
  errors: string[]
}

// ─── Window Lifecycle ───────────────────────────────────────────────────────

/**
 * Open a submission window for accepting files.
 */
export async function openWindow(
  windowId: string,
  actorId: string,
  prisma: PrismaClient,
): Promise<WindowLifecycleResult> {
  try {
    const window = await prisma.submissionWindow.findUnique({ where: { id: windowId } })

    if (!window) {
      return { success: false, errors: [`Submission window ${windowId} not found`] }
    }

    if (window.isLocked) {
      return { success: false, errors: ['Cannot open a locked window'] }
    }

    await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
      await tx.submissionWindow.update({
        where: { id: windowId },
        data: {
          windowOpenAt: new Date(),
          isLocked: false,
        },
      })

      await tx.decisionAuditLog.create({
        data: {
          eventType: 'submission_window.opened',
          entityType: 'SubmissionWindow',
          entityId: windowId,
          actorId,
          detailsJson: { windowName: window.name },
          snapshotJson: { timestamp: new Date().toISOString(), emittedBy: 'submission-manager' },
        },
      })

      await logAudit({
        prisma: tx,
        userId: actorId,
        action: 'SUBMISSION_WINDOW_OPEN',
        entityType: 'SubmissionWindow',
        entityId: windowId,
        detailsJson: { name: window.name },
      })
    })

    return { success: true }
  } catch (error) {
    console.error('[SubmissionManager] openWindow failed:', error)
    return {
      success: false,
      errors: [error instanceof Error ? error.message : 'Unknown error'],
    }
  }
}

/**
 * Close a submission window (respects deadline policy).
 */
export async function closeWindow(
  windowId: string,
  actorId: string,
  prisma: PrismaClient,
): Promise<WindowLifecycleResult> {
  try {
    const window = await prisma.submissionWindow.findUnique({ where: { id: windowId } })

    if (!window) {
      return { success: false, errors: [`Submission window ${windowId} not found`] }
    }

    await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
      const data: Record<string, unknown> = {
        windowCloseAt: new Date(),
      }

      // Auto-lock on close if configured
      if (window.lockOnClose && window.deadlinePolicy === 'HARD_DEADLINE') {
        data.isLocked = true
      }

      await tx.submissionWindow.update({ where: { id: windowId }, data })

      await tx.decisionAuditLog.create({
        data: {
          eventType: 'submission_window.closed',
          entityType: 'SubmissionWindow',
          entityId: windowId,
          actorId,
          detailsJson: {
            windowName: window.name,
            deadlinePolicy: window.deadlinePolicy,
            autoLocked: data.isLocked === true,
          },
          snapshotJson: { timestamp: new Date().toISOString(), emittedBy: 'submission-manager' },
        },
      })

      await logAudit({
        prisma: tx,
        userId: actorId,
        action: 'SUBMISSION_WINDOW_CLOSE',
        entityType: 'SubmissionWindow',
        entityId: windowId,
        detailsJson: { name: window.name },
      })
    })

    return { success: true }
  } catch (error) {
    console.error('[SubmissionManager] closeWindow failed:', error)
    return {
      success: false,
      errors: [error instanceof Error ? error.message : 'Unknown error'],
    }
  }
}

/**
 * Lock a submission window (no further uploads allowed).
 */
export async function lockWindow(
  windowId: string,
  actorId: string,
  prisma: PrismaClient,
): Promise<WindowLifecycleResult> {
  try {
    const window = await prisma.submissionWindow.findUnique({ where: { id: windowId } })

    if (!window) {
      return { success: false, errors: [`Submission window ${windowId} not found`] }
    }

    if (window.isLocked) {
      return { success: false, errors: ['Window is already locked'] }
    }

    await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
      await tx.submissionWindow.update({
        where: { id: windowId },
        data: { isLocked: true },
      })

      await tx.decisionAuditLog.create({
        data: {
          eventType: 'submission_window.locked',
          entityType: 'SubmissionWindow',
          entityId: windowId,
          actorId,
          detailsJson: { windowName: window.name },
          snapshotJson: { timestamp: new Date().toISOString(), emittedBy: 'submission-manager' },
        },
      })

      await logAudit({
        prisma: tx,
        userId: actorId,
        action: 'SUBMISSION_WINDOW_LOCK',
        entityType: 'SubmissionWindow',
        entityId: windowId,
        detailsJson: { name: window.name },
      })
    })

    return { success: true }
  } catch (error) {
    console.error('[SubmissionManager] lockWindow failed:', error)
    return {
      success: false,
      errors: [error instanceof Error ? error.message : 'Unknown error'],
    }
  }
}

// ─── Deadline Enforcement ───────────────────────────────────────────────────

/**
 * Check the current deadline status of a submission window.
 */
export async function checkDeadlinePolicy(
  windowId: string,
  prisma: PrismaClient,
): Promise<DeadlineStatus> {
  const window = await prisma.submissionWindow.findUnique({ where: { id: windowId } })

  if (!window) {
    return { status: 'LOCKED', deadlinePolicy: 'HARD_DEADLINE' }
  }

  if (window.isLocked) {
    return { status: 'LOCKED', deadlinePolicy: window.deadlinePolicy }
  }

  const now = new Date()

  // Not yet open
  if (window.windowOpenAt && now < window.windowOpenAt) {
    return { status: 'CLOSED', deadlinePolicy: window.deadlinePolicy }
  }

  // No close time or still before close
  if (!window.windowCloseAt || now < window.windowCloseAt) {
    return { status: 'OPEN', deadlinePolicy: window.deadlinePolicy }
  }

  // Past the close time — policy determines behavior
  switch (window.deadlinePolicy) {
    case 'HARD_DEADLINE':
      return { status: 'CLOSED', deadlinePolicy: window.deadlinePolicy }

    case 'FLAG':
      // Allow uploads but flag them
      return { status: 'OPEN', deadlinePolicy: window.deadlinePolicy }

    case 'GRACE': {
      if (window.graceHours) {
        const graceEnd = new Date(window.windowCloseAt.getTime() + window.graceHours * 60 * 60 * 1000)
        if (now < graceEnd) {
          return {
            status: 'GRACE',
            graceExpiresAt: graceEnd,
            deadlinePolicy: window.deadlinePolicy,
          }
        }
      }
      return { status: 'CLOSED', deadlinePolicy: window.deadlinePolicy }
    }

    default:
      return { status: 'CLOSED', deadlinePolicy: window.deadlinePolicy }
  }
}

// ─── File Requirement Validation ────────────────────────────────────────────

/**
 * Validate a project's submission against the window's file requirements.
 */
export async function validateSubmission(
  projectId: string,
  windowId: string,
  files: Array<{ mimeType: string; size: number; requirementId?: string }>,
  prisma: PrismaClient,
): Promise<SubmissionValidationResult> {
  const errors: string[] = []

  const requirements = await prisma.submissionFileRequirement.findMany({
    where: { submissionWindowId: windowId },
    orderBy: { sortOrder: 'asc' },
  })

  // Check required files are present
  for (const req of requirements) {
    if (!req.required) continue

    const matchingFiles = files.filter((f) => f.requirementId === req.id)
    if (matchingFiles.length === 0) {
      errors.push(`Missing required file: ${req.label}`)
    }
  }

  // Validate each file against its requirement
  for (const file of files) {
    if (!file.requirementId) continue

    const req = requirements.find((r: any) => r.id === file.requirementId)
    if (!req) {
      errors.push(`Unknown file requirement: ${file.requirementId}`)
      continue
    }

    // Check mime type
    if (req.mimeTypes.length > 0 && !req.mimeTypes.includes(file.mimeType)) {
      errors.push(
        `File for "${req.label}" has invalid type ${file.mimeType}. Allowed: ${req.mimeTypes.join(', ')}`,
      )
    }

    // Check size
    if (req.maxSizeMb && file.size > req.maxSizeMb * 1024 * 1024) {
      errors.push(
        `File for "${req.label}" exceeds max size of ${req.maxSizeMb}MB`,
      )
    }
  }

  return { valid: errors.length === 0, errors }
}

// ─── Read-Only Enforcement ──────────────────────────────────────────────────

/**
 * Check if a window is read-only (locked or hard-closed).
 */
export async function isWindowReadOnly(
  windowId: string,
  prisma: PrismaClient,
): Promise<boolean> {
  const status = await checkDeadlinePolicy(windowId, prisma)
  return status.status === 'LOCKED' || status.status === 'CLOSED'
}

// ─── Visibility Helpers ─────────────────────────────────────────────────────

/**
 * Get visible submission windows for a round.
 */
export async function getVisibleWindows(
  roundId: string,
  prisma: PrismaClient,
) {
  const visibility = await prisma.roundSubmissionVisibility.findMany({
    where: { roundId, canView: true },
    include: {
      submissionWindow: {
        include: { fileRequirements: { orderBy: { sortOrder: 'asc' } } },
      },
    },
  })

  return visibility.map((v: any) => ({
    ...v.submissionWindow,
    displayLabel: v.displayLabel,
  }))
}
