import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { Prisma } from '@prisma/client'
import { router, publicProcedure, adminProcedure } from '../trpc'
import { getPresignedUrl } from '@/lib/minio'

// Bucket for form submission files
export const SUBMISSIONS_BUCKET = 'mopc-submissions'

// Field type enum matching Prisma
const fieldTypeEnum = z.enum([
  'TEXT',
  'TEXTAREA',
  'NUMBER',
  'EMAIL',
  'PHONE',
  'URL',
  'DATE',
  'DATETIME',
  'SELECT',
  'MULTI_SELECT',
  'RADIO',
  'CHECKBOX',
  'CHECKBOX_GROUP',
  'FILE',
  'FILE_MULTIPLE',
  'SECTION',
  'INSTRUCTIONS',
])

// Special field type enum
const specialFieldTypeEnum = z.enum([
  'TEAM_MEMBERS',
  'COMPETITION_CATEGORY',
  'OCEAN_ISSUE',
  'FILE_UPLOAD',
  'GDPR_CONSENT',
  'COUNTRY_SELECT',
])

// Field input schema
const fieldInputSchema = z.object({
  fieldType: fieldTypeEnum,
  name: z.string().min(1).max(100),
  label: z.string().min(1).max(255),
  description: z.string().optional(),
  placeholder: z.string().optional(),
  required: z.boolean().default(false),
  minLength: z.number().int().optional(),
  maxLength: z.number().int().optional(),
  minValue: z.number().optional(),
  maxValue: z.number().optional(),
  optionsJson: z
    .array(z.object({ value: z.string(), label: z.string() }))
    .optional(),
  conditionJson: z
    .object({
      fieldId: z.string(),
      operator: z.enum(['equals', 'not_equals', 'contains', 'not_empty']),
      value: z.string().optional(),
    })
    .optional(),
  sortOrder: z.number().int().default(0),
  width: z.enum(['full', 'half']).default('full'),
  // Onboarding-specific fields
  stepId: z.string().optional(),
  projectMapping: z.string().optional(),
  specialType: specialFieldTypeEnum.optional(),
})

// Step input schema
const stepInputSchema = z.object({
  name: z.string().min(1).max(100),
  title: z.string().min(1).max(255),
  description: z.string().optional(),
  isOptional: z.boolean().default(false),
  conditionJson: z
    .object({
      fieldId: z.string(),
      operator: z.enum(['equals', 'not_equals', 'contains', 'not_empty']),
      value: z.string().optional(),
    })
    .optional(),
})

export const applicationFormRouter = router({
  /**
   * List all forms (admin view)
   */
  list: adminProcedure
    .input(
      z.object({
        programId: z.string().optional(),
        status: z.enum(['DRAFT', 'PUBLISHED', 'CLOSED']).optional(),
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {}

      if (input.programId !== undefined) {
        where.programId = input.programId
      }
      if (input.status) {
        where.status = input.status
      }

      const [data, total] = await Promise.all([
        ctx.prisma.applicationForm.findMany({
          where,
          include: {
            program: { select: { id: true, name: true, year: true } },
            _count: { select: { submissions: true, fields: true } },
          },
          orderBy: { createdAt: 'desc' },
          skip: (input.page - 1) * input.perPage,
          take: input.perPage,
        }),
        ctx.prisma.applicationForm.count({ where }),
      ])

      return {
        data,
        total,
        page: input.page,
        perPage: input.perPage,
        totalPages: Math.ceil(total / input.perPage),
      }
    }),

  /**
   * Get a single form by ID (admin view with all fields and steps)
   */
  get: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.applicationForm.findUniqueOrThrow({
        where: { id: input.id },
        include: {
          program: { select: { id: true, name: true, year: true } },
          round: { select: { id: true, name: true, slug: true } },
          fields: { orderBy: { sortOrder: 'asc' } },
          steps: {
            orderBy: { sortOrder: 'asc' },
            include: { fields: { orderBy: { sortOrder: 'asc' } } },
          },
          _count: { select: { submissions: true } },
        },
      })
    }),

  /**
   * Get a public form by slug (for form submission)
   */
  getBySlug: publicProcedure
    .input(z.object({ slug: z.string() }))
    .query(async ({ ctx, input }) => {
      const form = await ctx.prisma.applicationForm.findUnique({
        where: { publicSlug: input.slug },
        include: {
          fields: { orderBy: { sortOrder: 'asc' } },
        },
      })

      if (!form) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Form not found',
        })
      }

      // Check if form is available
      if (!form.isPublic || form.status !== 'PUBLISHED') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form is not currently accepting submissions',
        })
      }

      // Check submission window
      const now = new Date()
      if (form.opensAt && now < form.opensAt) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form is not yet open for submissions',
        })
      }
      if (form.closesAt && now > form.closesAt) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form has closed',
        })
      }

      // Check submission limit
      if (form.submissionLimit) {
        const count = await ctx.prisma.applicationFormSubmission.count({
          where: { formId: form.id },
        })
        if (count >= form.submissionLimit) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'This form has reached its submission limit',
          })
        }
      }

      return form
    }),

  /**
   * Create a new form (admin only)
   */
  create: adminProcedure
    .input(
      z.object({
        programId: z.string().nullable(),
        name: z.string().min(1).max(255),
        description: z.string().optional(),
        publicSlug: z.string().min(1).max(100).optional(),
        submissionLimit: z.number().int().optional(),
        opensAt: z.string().datetime().optional(),
        closesAt: z.string().datetime().optional(),
        confirmationMessage: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Check slug uniqueness
      if (input.publicSlug) {
        const existing = await ctx.prisma.applicationForm.findUnique({
          where: { publicSlug: input.publicSlug },
        })
        if (existing) {
          throw new TRPCError({
            code: 'CONFLICT',
            message: 'This URL slug is already in use',
          })
        }
      }

      const form = await ctx.prisma.applicationForm.create({
        data: {
          ...input,
          opensAt: input.opensAt ? new Date(input.opensAt) : null,
          closesAt: input.closesAt ? new Date(input.closesAt) : null,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'CREATE',
          entityType: 'ApplicationForm',
          entityId: form.id,
          detailsJson: { name: input.name },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return form
    }),

  /**
   * Update a form (admin only)
   */
  update: adminProcedure
    .input(
      z.object({
        id: z.string(),
        name: z.string().min(1).max(255).optional(),
        description: z.string().optional().nullable(),
        status: z.enum(['DRAFT', 'PUBLISHED', 'CLOSED']).optional(),
        isPublic: z.boolean().optional(),
        publicSlug: z.string().min(1).max(100).optional().nullable(),
        submissionLimit: z.number().int().optional().nullable(),
        opensAt: z.string().datetime().optional().nullable(),
        closesAt: z.string().datetime().optional().nullable(),
        confirmationMessage: z.string().optional().nullable(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, opensAt, closesAt, ...data } = input

      // Check slug uniqueness if changing
      if (data.publicSlug) {
        const existing = await ctx.prisma.applicationForm.findFirst({
          where: { publicSlug: data.publicSlug, NOT: { id } },
        })
        if (existing) {
          throw new TRPCError({
            code: 'CONFLICT',
            message: 'This URL slug is already in use',
          })
        }
      }

      const form = await ctx.prisma.applicationForm.update({
        where: { id },
        data: {
          ...data,
          opensAt: opensAt ? new Date(opensAt) : opensAt === null ? null : undefined,
          closesAt: closesAt ? new Date(closesAt) : closesAt === null ? null : undefined,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'UPDATE',
          entityType: 'ApplicationForm',
          entityId: id,
          detailsJson: data,
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return form
    }),

  /**
   * Delete a form (admin only)
   */
  delete: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const form = await ctx.prisma.applicationForm.delete({
        where: { id: input.id },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DELETE',
          entityType: 'ApplicationForm',
          entityId: input.id,
          detailsJson: { name: form.name },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return form
    }),

  /**
   * Add a field to a form (or step)
   */
  addField: adminProcedure
    .input(
      z.object({
        formId: z.string(),
        field: fieldInputSchema,
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get max sort order (within the step if specified, otherwise form-wide)
      const whereClause = input.field.stepId
        ? { stepId: input.field.stepId }
        : { formId: input.formId, stepId: null }

      const maxOrder = await ctx.prisma.applicationFormField.aggregate({
        where: whereClause,
        _max: { sortOrder: true },
      })

      const { stepId, projectMapping, specialType, ...restField } = input.field

      const field = await ctx.prisma.applicationFormField.create({
        data: {
          formId: input.formId,
          ...restField,
          sortOrder: restField.sortOrder ?? (maxOrder._max.sortOrder ?? 0) + 1,
          optionsJson: restField.optionsJson ?? undefined,
          conditionJson: restField.conditionJson ?? undefined,
          stepId: stepId ?? undefined,
          projectMapping: projectMapping ?? undefined,
          specialType: specialType ?? undefined,
        },
      })

      return field
    }),

  /**
   * Update a field
   */
  updateField: adminProcedure
    .input(
      z.object({
        id: z.string(),
        field: fieldInputSchema.partial(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { stepId, projectMapping, specialType, ...restField } = input.field

      const field = await ctx.prisma.applicationFormField.update({
        where: { id: input.id },
        data: {
          ...restField,
          optionsJson: restField.optionsJson ?? undefined,
          conditionJson: restField.conditionJson ?? undefined,
          // Handle nullable fields explicitly
          stepId: stepId === undefined ? undefined : stepId,
          projectMapping: projectMapping === undefined ? undefined : projectMapping,
          specialType: specialType === undefined ? undefined : specialType,
        },
      })

      return field
    }),

  /**
   * Delete a field
   */
  deleteField: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      return ctx.prisma.applicationFormField.delete({
        where: { id: input.id },
      })
    }),

  /**
   * Reorder fields
   */
  reorderFields: adminProcedure
    .input(
      z.object({
        items: z.array(
          z.object({
            id: z.string(),
            sortOrder: z.number().int(),
          })
        ),
      })
    )
    .mutation(async ({ ctx, input }) => {
      await ctx.prisma.$transaction(
        input.items.map((item) =>
          ctx.prisma.applicationFormField.update({
            where: { id: item.id },
            data: { sortOrder: item.sortOrder },
          })
        )
      )

      return { success: true }
    }),

  /**
   * Submit a form (public endpoint)
   */
  submit: publicProcedure
    .input(
      z.object({
        formId: z.string(),
        data: z.record(z.unknown()),
        email: z.string().email().optional(),
        name: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get form with fields
      const form = await ctx.prisma.applicationForm.findUniqueOrThrow({
        where: { id: input.formId },
        include: { fields: true },
      })

      // Verify form is accepting submissions
      if (!form.isPublic || form.status !== 'PUBLISHED') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form is not accepting submissions',
        })
      }

      // Check submission window
      const now = new Date()
      if (form.opensAt && now < form.opensAt) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form is not yet open',
        })
      }
      if (form.closesAt && now > form.closesAt) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form has closed',
        })
      }

      // Check submission limit
      if (form.submissionLimit) {
        const count = await ctx.prisma.applicationFormSubmission.count({
          where: { formId: form.id },
        })
        if (count >= form.submissionLimit) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'This form has reached its submission limit',
          })
        }
      }

      // Validate required fields
      for (const field of form.fields) {
        if (field.required && field.fieldType !== 'SECTION' && field.fieldType !== 'INSTRUCTIONS') {
          const value = input.data[field.name]
          if (value === undefined || value === null || value === '') {
            throw new TRPCError({
              code: 'BAD_REQUEST',
              message: `${field.label} is required`,
            })
          }
        }
      }

      // Create submission
      const submission = await ctx.prisma.applicationFormSubmission.create({
        data: {
          formId: input.formId,
          email: input.email,
          name: input.name,
          dataJson: input.data as Prisma.InputJsonValue,
        },
      })

      return {
        success: true,
        submissionId: submission.id,
        confirmationMessage: form.confirmationMessage,
      }
    }),

  /**
   * Get upload URL for a submission file
   */
  getSubmissionUploadUrl: publicProcedure
    .input(
      z.object({
        formId: z.string(),
        fieldName: z.string(),
        fileName: z.string(),
        mimeType: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify form exists and is accepting submissions
      const form = await ctx.prisma.applicationForm.findUniqueOrThrow({
        where: { id: input.formId },
      })

      if (!form.isPublic || form.status !== 'PUBLISHED') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'This form is not accepting submissions',
        })
      }

      const timestamp = Date.now()
      const sanitizedName = input.fileName.replace(/[^a-zA-Z0-9.-]/g, '_')
      const objectKey = `forms/${input.formId}/${timestamp}-${sanitizedName}`

      const url = await getPresignedUrl(SUBMISSIONS_BUCKET, objectKey, 'PUT', 3600)

      return {
        url,
        bucket: SUBMISSIONS_BUCKET,
        objectKey,
      }
    }),

  /**
   * List submissions for a form (admin only)
   */
  listSubmissions: adminProcedure
    .input(
      z.object({
        formId: z.string(),
        status: z.enum(['SUBMITTED', 'REVIEWED', 'APPROVED', 'REJECTED']).optional(),
        search: z.string().optional(),
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {
        formId: input.formId,
      }

      if (input.status) {
        where.status = input.status
      }

      if (input.search) {
        where.OR = [
          { email: { contains: input.search, mode: 'insensitive' } },
          { name: { contains: input.search, mode: 'insensitive' } },
        ]
      }

      const [data, total] = await Promise.all([
        ctx.prisma.applicationFormSubmission.findMany({
          where,
          include: {
            files: true,
          },
          orderBy: { createdAt: 'desc' },
          skip: (input.page - 1) * input.perPage,
          take: input.perPage,
        }),
        ctx.prisma.applicationFormSubmission.count({ where }),
      ])

      return {
        data,
        total,
        page: input.page,
        perPage: input.perPage,
        totalPages: Math.ceil(total / input.perPage),
      }
    }),

  /**
   * Get a single submission
   */
  getSubmission: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.applicationFormSubmission.findUniqueOrThrow({
        where: { id: input.id },
        include: {
          form: {
            include: { fields: { orderBy: { sortOrder: 'asc' } } },
          },
          files: true,
        },
      })
    }),

  /**
   * Update submission status
   */
  updateSubmissionStatus: adminProcedure
    .input(
      z.object({
        id: z.string(),
        status: z.enum(['SUBMITTED', 'REVIEWED', 'APPROVED', 'REJECTED']),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const submission = await ctx.prisma.applicationFormSubmission.update({
        where: { id: input.id },
        data: { status: input.status },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'UPDATE_STATUS',
          entityType: 'ApplicationFormSubmission',
          entityId: input.id,
          detailsJson: { status: input.status },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return submission
    }),

  /**
   * Delete a submission
   */
  deleteSubmission: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const submission = await ctx.prisma.applicationFormSubmission.delete({
        where: { id: input.id },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DELETE',
          entityType: 'ApplicationFormSubmission',
          entityId: input.id,
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return submission
    }),

  /**
   * Get download URL for a submission file
   */
  getSubmissionFileUrl: adminProcedure
    .input(z.object({ fileId: z.string() }))
    .query(async ({ ctx, input }) => {
      const file = await ctx.prisma.submissionFile.findUniqueOrThrow({
        where: { id: input.fileId },
      })

      const url = await getPresignedUrl(file.bucket, file.objectKey, 'GET', 900)
      return { url, fileName: file.fileName }
    }),

  /**
   * Duplicate a form
   */
  duplicate: adminProcedure
    .input(
      z.object({
        id: z.string(),
        name: z.string().min(1).max(255),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get original form with fields
      const original = await ctx.prisma.applicationForm.findUniqueOrThrow({
        where: { id: input.id },
        include: { fields: true },
      })

      // Create new form
      const newForm = await ctx.prisma.applicationForm.create({
        data: {
          programId: original.programId,
          name: input.name,
          description: original.description,
          status: 'DRAFT',
          isPublic: false,
          confirmationMessage: original.confirmationMessage,
        },
      })

      // Copy fields
      await ctx.prisma.applicationFormField.createMany({
        data: original.fields.map((field) => ({
          formId: newForm.id,
          fieldType: field.fieldType,
          name: field.name,
          label: field.label,
          description: field.description,
          placeholder: field.placeholder,
          required: field.required,
          minLength: field.minLength,
          maxLength: field.maxLength,
          minValue: field.minValue,
          maxValue: field.maxValue,
          optionsJson: field.optionsJson as Prisma.InputJsonValue ?? undefined,
          conditionJson: field.conditionJson as Prisma.InputJsonValue ?? undefined,
          sortOrder: field.sortOrder,
          width: field.width,
        })),
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DUPLICATE',
          entityType: 'ApplicationForm',
          entityId: newForm.id,
          detailsJson: { originalId: input.id, name: input.name },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return newForm
    }),

  // ===========================================================================
  // ONBOARDING STEP MANAGEMENT
  // ===========================================================================

  /**
   * Create a new step in a form
   */
  createStep: adminProcedure
    .input(
      z.object({
        formId: z.string(),
        step: stepInputSchema,
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get max sort order
      const maxOrder = await ctx.prisma.onboardingStep.aggregate({
        where: { formId: input.formId },
        _max: { sortOrder: true },
      })

      const step = await ctx.prisma.onboardingStep.create({
        data: {
          formId: input.formId,
          ...input.step,
          sortOrder: (maxOrder._max.sortOrder ?? -1) + 1,
          conditionJson: input.step.conditionJson ?? undefined,
        },
      })

      return step
    }),

  /**
   * Update a step
   */
  updateStep: adminProcedure
    .input(
      z.object({
        id: z.string(),
        step: stepInputSchema.partial(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const step = await ctx.prisma.onboardingStep.update({
        where: { id: input.id },
        data: {
          ...input.step,
          conditionJson: input.step.conditionJson ?? undefined,
        },
      })

      return step
    }),

  /**
   * Delete a step (fields will have stepId set to null)
   */
  deleteStep: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      return ctx.prisma.onboardingStep.delete({
        where: { id: input.id },
      })
    }),

  /**
   * Reorder steps
   */
  reorderSteps: adminProcedure
    .input(
      z.object({
        formId: z.string(),
        stepIds: z.array(z.string()),
      })
    )
    .mutation(async ({ ctx, input }) => {
      await ctx.prisma.$transaction(
        input.stepIds.map((id, index) =>
          ctx.prisma.onboardingStep.update({
            where: { id },
            data: { sortOrder: index },
          })
        )
      )

      return { success: true }
    }),

  /**
   * Move a field to a different step
   */
  moveFieldToStep: adminProcedure
    .input(
      z.object({
        fieldId: z.string(),
        stepId: z.string().nullable(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const field = await ctx.prisma.applicationFormField.update({
        where: { id: input.fieldId },
        data: { stepId: input.stepId },
      })

      return field
    }),

  /**
   * Update email settings for a form
   */
  updateEmailSettings: adminProcedure
    .input(
      z.object({
        formId: z.string(),
        sendConfirmationEmail: z.boolean().optional(),
        sendTeamInviteEmails: z.boolean().optional(),
        confirmationEmailSubject: z.string().optional().nullable(),
        confirmationEmailBody: z.string().optional().nullable(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { formId, ...data } = input

      const form = await ctx.prisma.applicationForm.update({
        where: { id: formId },
        data,
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'UPDATE_EMAIL_SETTINGS',
          entityType: 'ApplicationForm',
          entityId: formId,
          detailsJson: data,
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return form
    }),

  /**
   * Link a form to a round (for onboarding forms that create projects)
   */
  linkToRound: adminProcedure
    .input(
      z.object({
        formId: z.string(),
        roundId: z.string().nullable(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Check if another form is already linked to this round
      if (input.roundId) {
        const existing = await ctx.prisma.applicationForm.findFirst({
          where: {
            roundId: input.roundId,
            NOT: { id: input.formId },
          },
        })
        if (existing) {
          throw new TRPCError({
            code: 'CONFLICT',
            message: 'Another form is already linked to this round',
          })
        }
      }

      const form = await ctx.prisma.applicationForm.update({
        where: { id: input.formId },
        data: { roundId: input.roundId },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'LINK_TO_ROUND',
          entityType: 'ApplicationForm',
          entityId: input.formId,
          detailsJson: { roundId: input.roundId },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return form
    }),

  /**
   * Get form with steps for onboarding builder
   */
  getForBuilder: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.applicationForm.findUniqueOrThrow({
        where: { id: input.id },
        include: {
          program: { select: { id: true, name: true, year: true } },
          round: { select: { id: true, name: true, slug: true, programId: true } },
          steps: {
            orderBy: { sortOrder: 'asc' },
            include: {
              fields: { orderBy: { sortOrder: 'asc' } },
            },
          },
          fields: {
            where: { stepId: null },
            orderBy: { sortOrder: 'asc' },
          },
          _count: { select: { submissions: true } },
        },
      })
    }),

  /**
   * Get available rounds for linking
   */
  getAvailableRounds: adminProcedure
    .input(z.object({ programId: z.string().optional() }))
    .query(async ({ ctx, input }) => {
      // Get rounds that don't have a linked form yet
      return ctx.prisma.round.findMany({
        where: {
          ...(input.programId ? { programId: input.programId } : {}),
          applicationForm: null,
        },
        select: {
          id: true,
          name: true,
          slug: true,
          status: true,
          program: { select: { id: true, name: true, year: true } },
        },
        orderBy: [{ program: { year: 'desc' } }, { sortOrder: 'asc' }],
      })
    }),

  /**
   * Get form statistics
   */
  getStats: adminProcedure
    .input(z.object({ formId: z.string() }))
    .query(async ({ ctx, input }) => {
      const [total, byStatus, recentSubmissions] = await Promise.all([
        ctx.prisma.applicationFormSubmission.count({
          where: { formId: input.formId },
        }),
        ctx.prisma.applicationFormSubmission.groupBy({
          by: ['status'],
          where: { formId: input.formId },
          _count: true,
        }),
        ctx.prisma.applicationFormSubmission.findMany({
          where: { formId: input.formId },
          select: { createdAt: true },
          orderBy: { createdAt: 'desc' },
          take: 30,
        }),
      ])

      // Calculate submissions per day for last 30 days
      const thirtyDaysAgo = new Date()
      thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)

      const submissionsPerDay = new Map<string, number>()
      for (const sub of recentSubmissions) {
        const date = sub.createdAt.toISOString().split('T')[0]
        submissionsPerDay.set(date, (submissionsPerDay.get(date) || 0) + 1)
      }

      return {
        total,
        byStatus: Object.fromEntries(
          byStatus.map((r) => [r.status, r._count])
        ),
        submissionsPerDay: Object.fromEntries(submissionsPerDay),
      }
    }),
})
