import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, publicProcedure, protectedProcedure } from '../trpc'
import { getPresignedUrl } from '@/lib/minio'

// Bucket for applicant submissions
export const SUBMISSIONS_BUCKET = 'mopc-submissions'

export const applicantRouter = router({
  /**
   * Get submission info for an applicant (by round slug)
   */
  getSubmissionBySlug: publicProcedure
    .input(z.object({ slug: z.string() }))
    .query(async ({ ctx, input }) => {
      // Find the round by slug
      const round = await ctx.prisma.round.findFirst({
        where: { slug: input.slug },
        include: {
          program: { select: { id: true, name: true, year: true, description: true } },
        },
      })

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

      // Check if submissions are open
      const now = new Date()
      const isOpen = round.submissionDeadline
        ? now < round.submissionDeadline
        : round.status === 'ACTIVE'

      return {
        round: {
          id: round.id,
          name: round.name,
          slug: round.slug,
          submissionDeadline: round.submissionDeadline,
          isOpen,
        },
        program: round.program,
      }
    }),

  /**
   * Get the current user's submission for a round (as submitter or team member)
   */
  getMySubmission: protectedProcedure
    .input(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      // Only applicants can use this
      if (ctx.user.role !== 'APPLICANT') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only applicants can access submissions',
        })
      }

      const project = await ctx.prisma.project.findFirst({
        where: {
          roundId: input.roundId,
          OR: [
            { submittedByUserId: ctx.user.id },
            {
              teamMembers: {
                some: { userId: ctx.user.id },
              },
            },
          ],
        },
        include: {
          files: true,
          round: {
            include: {
              program: { select: { name: true, year: true } },
            },
          },
          teamMembers: {
            include: {
              user: {
                select: { id: true, name: true, email: true },
              },
            },
          },
        },
      })

      if (project) {
        const userMembership = project.teamMembers.find((tm) => tm.userId === ctx.user.id)
        return {
          ...project,
          userRole: userMembership?.role || (project.submittedByUserId === ctx.user.id ? 'LEAD' : null),
          isTeamLead: project.submittedByUserId === ctx.user.id || userMembership?.role === 'LEAD',
        }
      }

      return null
    }),

  /**
   * Create or update a submission (draft or submitted)
   */
  saveSubmission: protectedProcedure
    .input(
      z.object({
        roundId: z.string(),
        projectId: z.string().optional(), // If updating existing
        title: z.string().min(1).max(500),
        teamName: z.string().optional(),
        description: z.string().optional(),
        tags: z.array(z.string()).optional(),
        metadataJson: z.record(z.unknown()).optional(),
        submit: z.boolean().default(false), // Whether to submit or just save draft
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Only applicants can use this
      if (ctx.user.role !== 'APPLICANT') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only applicants can submit projects',
        })
      }

      // Check if the round is open for submissions
      const round = await ctx.prisma.round.findUniqueOrThrow({
        where: { id: input.roundId },
      })

      const now = new Date()
      if (round.submissionDeadline && now > round.submissionDeadline) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Submission deadline has passed',
        })
      }

      const { projectId, submit, roundId, metadataJson, ...data } = input

      if (projectId) {
        // Update existing
        const existing = await ctx.prisma.project.findFirst({
          where: {
            id: projectId,
            submittedByUserId: ctx.user.id,
          },
        })

        if (!existing) {
          throw new TRPCError({
            code: 'NOT_FOUND',
            message: 'Project not found or you do not have access',
          })
        }

        // Can't update if already submitted
        if (existing.submittedAt && !submit) {
          throw new TRPCError({
            code: 'BAD_REQUEST',
            message: 'Cannot modify a submitted project',
          })
        }

        const project = await ctx.prisma.project.update({
          where: { id: projectId },
          data: {
            ...data,
            metadataJson: metadataJson as unknown ?? undefined,
            submittedAt: submit && !existing.submittedAt ? now : existing.submittedAt,
          },
        })

        // Update Project status if submitting
        if (submit) {
          await ctx.prisma.project.update({
            where: { id: projectId },
            data: { status: 'SUBMITTED' },
          })
        }

        return project
      } else {
        // Get the round to find the programId
        const roundForCreate = await ctx.prisma.round.findUniqueOrThrow({
          where: { id: roundId },
          select: { programId: true },
        })

        // Create new project
        const project = await ctx.prisma.project.create({
          data: {
            roundId,
            ...data,
            metadataJson: metadataJson as unknown ?? undefined,
            submittedByUserId: ctx.user.id,
            submittedByEmail: ctx.user.email,
            submissionSource: 'MANUAL',
            submittedAt: submit ? now : null,
            status: 'SUBMITTED',
          },
        })

        // Audit log
        await ctx.prisma.auditLog.create({
          data: {
            userId: ctx.user.id,
            action: 'CREATE',
            entityType: 'Project',
            entityId: project.id,
            detailsJson: { title: input.title, source: 'applicant_portal' },
            ipAddress: ctx.ip,
            userAgent: ctx.userAgent,
          },
        })

        return project
      }
    }),

  /**
   * Get upload URL for a submission file
   */
  getUploadUrl: protectedProcedure
    .input(
      z.object({
        projectId: z.string(),
        fileName: z.string(),
        mimeType: z.string(),
        fileType: z.enum(['EXEC_SUMMARY', 'BUSINESS_PLAN', 'VIDEO_PITCH', 'PRESENTATION', 'SUPPORTING_DOC', 'OTHER']),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Only applicants can use this
      if (ctx.user.role !== 'APPLICANT') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only applicants can upload files',
        })
      }

      // Verify project ownership
      const project = await ctx.prisma.project.findFirst({
        where: {
          id: input.projectId,
          submittedByUserId: ctx.user.id,
        },
      })

      if (!project) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Project not found or you do not have access',
        })
      }

      // Can't upload if already submitted
      if (project.submittedAt) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Cannot modify a submitted project',
        })
      }

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

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

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

  /**
   * Save file metadata after upload
   */
  saveFileMetadata: protectedProcedure
    .input(
      z.object({
        projectId: z.string(),
        fileName: z.string(),
        mimeType: z.string(),
        size: z.number().int(),
        fileType: z.enum(['EXEC_SUMMARY', 'BUSINESS_PLAN', 'VIDEO_PITCH', 'PRESENTATION', 'SUPPORTING_DOC', 'OTHER']),
        bucket: z.string(),
        objectKey: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Only applicants can use this
      if (ctx.user.role !== 'APPLICANT') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only applicants can save files',
        })
      }

      // Verify project ownership
      const project = await ctx.prisma.project.findFirst({
        where: {
          id: input.projectId,
          submittedByUserId: ctx.user.id,
        },
      })

      if (!project) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Project not found or you do not have access',
        })
      }

      const { projectId, ...fileData } = input

      // Delete existing file of same type if exists
      await ctx.prisma.projectFile.deleteMany({
        where: {
          projectId,
          fileType: input.fileType,
        },
      })

      // Create new file record
      const file = await ctx.prisma.projectFile.create({
        data: {
          projectId,
          ...fileData,
        },
      })

      return file
    }),

  /**
   * Delete a file from submission
   */
  deleteFile: protectedProcedure
    .input(z.object({ fileId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      // Only applicants can use this
      if (ctx.user.role !== 'APPLICANT') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only applicants can delete files',
        })
      }

      const file = await ctx.prisma.projectFile.findUniqueOrThrow({
        where: { id: input.fileId },
        include: { project: true },
      })

      // Verify ownership
      if (file.project.submittedByUserId !== ctx.user.id) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'You do not have access to this file',
        })
      }

      // Can't delete if project is submitted
      if (file.project.submittedAt) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Cannot modify a submitted project',
        })
      }

      await ctx.prisma.projectFile.delete({
        where: { id: input.fileId },
      })

      return { success: true }
    }),

  /**
   * Get submission status timeline
   */
  getSubmissionStatus: protectedProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      const project = await ctx.prisma.project.findFirst({
        where: {
          id: input.projectId,
          OR: [
            { submittedByUserId: ctx.user.id },
            {
              teamMembers: {
                some: { userId: ctx.user.id },
              },
            },
          ],
        },
        include: {
          round: {
            include: {
              program: { select: { name: true, year: true } },
            },
          },
          files: true,
          teamMembers: {
            include: {
              user: {
                select: { id: true, name: true, email: true },
              },
            },
          },
        },
      })

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

      // Get the project status
      const currentStatus = project.status ?? 'SUBMITTED'

      // Build timeline
      const timeline = [
        {
          status: 'CREATED',
          label: 'Application Started',
          date: project.createdAt,
          completed: true,
        },
        {
          status: 'SUBMITTED',
          label: 'Application Submitted',
          date: project.submittedAt,
          completed: !!project.submittedAt,
        },
        {
          status: 'UNDER_REVIEW',
          label: 'Under Review',
          date: currentStatus === 'SUBMITTED' && project.submittedAt ? project.submittedAt : null,
          completed: ['UNDER_REVIEW', 'SEMIFINALIST', 'FINALIST', 'WINNER'].includes(currentStatus),
        },
        {
          status: 'SEMIFINALIST',
          label: 'Semi-finalist',
          date: null, // Would need status change tracking
          completed: ['SEMIFINALIST', 'FINALIST', 'WINNER'].includes(currentStatus),
        },
        {
          status: 'FINALIST',
          label: 'Finalist',
          date: null,
          completed: ['FINALIST', 'WINNER'].includes(currentStatus),
        },
      ]

      return {
        project,
        timeline,
        currentStatus,
      }
    }),

  /**
   * List all submissions for current user (including as team member)
   */
  listMySubmissions: protectedProcedure.query(async ({ ctx }) => {
    if (ctx.user.role !== 'APPLICANT') {
      throw new TRPCError({
        code: 'FORBIDDEN',
        message: 'Only applicants can access submissions',
      })
    }

    // Find projects where user is either the submitter OR a team member
    const projects = await ctx.prisma.project.findMany({
      where: {
        OR: [
          { submittedByUserId: ctx.user.id },
          {
            teamMembers: {
              some: { userId: ctx.user.id },
            },
          },
        ],
      },
      include: {
        round: {
          include: {
            program: { select: { name: true, year: true } },
          },
        },
        files: true,
        teamMembers: {
          include: {
            user: {
              select: { id: true, name: true, email: true },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    })

    // Add user's role in each project
    return projects.map((project) => {
      const userMembership = project.teamMembers.find((tm) => tm.userId === ctx.user.id)
      return {
        ...project,
        userRole: userMembership?.role || (project.submittedByUserId === ctx.user.id ? 'LEAD' : null),
        isTeamLead: project.submittedByUserId === ctx.user.id || userMembership?.role === 'LEAD',
      }
    })
  }),

  /**
   * Get team members for a project
   */
  getTeamMembers: protectedProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      // Verify user has access to this project
      const project = await ctx.prisma.project.findFirst({
        where: {
          id: input.projectId,
          OR: [
            { submittedByUserId: ctx.user.id },
            {
              teamMembers: {
                some: { userId: ctx.user.id },
              },
            },
          ],
        },
        include: {
          teamMembers: {
            include: {
              user: {
                select: {
                  id: true,
                  name: true,
                  email: true,
                  status: true,
                  lastLoginAt: true,
                },
              },
            },
            orderBy: { joinedAt: 'asc' },
          },
          submittedBy: {
            select: { id: true, name: true, email: true },
          },
        },
      })

      if (!project) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Project not found or you do not have access',
        })
      }

      return {
        teamMembers: project.teamMembers,
        submittedBy: project.submittedBy,
      }
    }),

  /**
   * Invite a new team member
   */
  inviteTeamMember: protectedProcedure
    .input(
      z.object({
        projectId: z.string(),
        email: z.string().email(),
        name: z.string().min(1),
        role: z.enum(['MEMBER', 'ADVISOR']),
        title: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify user is team lead
      const project = await ctx.prisma.project.findFirst({
        where: {
          id: input.projectId,
          OR: [
            { submittedByUserId: ctx.user.id },
            {
              teamMembers: {
                some: {
                  userId: ctx.user.id,
                  role: 'LEAD',
                },
              },
            },
          ],
        },
      })

      if (!project) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only team leads can invite new members',
        })
      }

      // Check if already a team member
      const existingMember = await ctx.prisma.teamMember.findFirst({
        where: {
          projectId: input.projectId,
          user: { email: input.email },
        },
      })

      if (existingMember) {
        throw new TRPCError({
          code: 'CONFLICT',
          message: 'This person is already a team member',
        })
      }

      // Find or create user
      let user = await ctx.prisma.user.findUnique({
        where: { email: input.email },
      })

      if (!user) {
        user = await ctx.prisma.user.create({
          data: {
            email: input.email,
            name: input.name,
            role: 'APPLICANT',
            status: 'INVITED',
          },
        })
      }

      // Create team membership
      const teamMember = await ctx.prisma.teamMember.create({
        data: {
          projectId: input.projectId,
          userId: user.id,
          role: input.role,
          title: input.title,
        },
        include: {
          user: {
            select: { id: true, name: true, email: true, status: true },
          },
        },
      })

      // TODO: Send invitation email to the new team member

      return teamMember
    }),

  /**
   * Remove a team member
   */
  removeTeamMember: protectedProcedure
    .input(
      z.object({
        projectId: z.string(),
        userId: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify user is team lead
      const project = await ctx.prisma.project.findFirst({
        where: {
          id: input.projectId,
          OR: [
            { submittedByUserId: ctx.user.id },
            {
              teamMembers: {
                some: {
                  userId: ctx.user.id,
                  role: 'LEAD',
                },
              },
            },
          ],
        },
      })

      if (!project) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only team leads can remove members',
        })
      }

      // Can't remove the original submitter
      if (project.submittedByUserId === input.userId) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Cannot remove the original applicant from the team',
        })
      }

      await ctx.prisma.teamMember.deleteMany({
        where: {
          projectId: input.projectId,
          userId: input.userId,
        },
      })

      return { success: true }
    }),
})
