import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { Prisma } from '@prisma/client'
import { router, publicProcedure } from '../trpc'
import { sendApplicationConfirmationEmail, sendTeamMemberInviteEmail } from '@/lib/email'
import { nanoid } from 'nanoid'
import {
  createNotification,
  notifyAdmins,
  NotificationTypes,
} from '../services/in-app-notification'

// Team member input for submission
const teamMemberInputSchema = z.object({
  name: z.string().min(1),
  email: z.string().email().optional(),
  title: z.string().optional(),
  role: z.enum(['LEAD', 'MEMBER', 'ADVISOR']).default('MEMBER'),
})

export const onboardingRouter = router({
  /**
   * Get onboarding form configuration for public wizard
   * Returns form + steps + fields + program info
   */
  getConfig: publicProcedure
    .input(
      z.object({
        slug: z.string(), // Round slug or form publicSlug
      })
    )
    .query(async ({ ctx, input }) => {
      // Try to find by round slug first
      let form = await ctx.prisma.applicationForm.findFirst({
        where: {
          round: { slug: input.slug },
          status: 'PUBLISHED',
          isPublic: true,
        },
        include: {
          program: { select: { id: true, name: true, year: true } },
          round: {
            select: {
              id: true,
              name: true,
              slug: true,
              submissionStartDate: true,
              submissionEndDate: true,
              submissionDeadline: true,
            },
          },
          steps: {
            orderBy: { sortOrder: 'asc' },
            include: {
              fields: { orderBy: { sortOrder: 'asc' } },
            },
          },
          fields: {
            where: { stepId: null },
            orderBy: { sortOrder: 'asc' },
          },
        },
      })

      // If not found by round slug, try form publicSlug
      if (!form) {
        form = await ctx.prisma.applicationForm.findFirst({
          where: {
            publicSlug: input.slug,
            status: 'PUBLISHED',
            isPublic: true,
          },
          include: {
            program: { select: { id: true, name: true, year: true } },
            round: {
              select: {
                id: true,
                name: true,
                slug: true,
                submissionStartDate: true,
                submissionEndDate: true,
                submissionDeadline: true,
              },
            },
            steps: {
              orderBy: { sortOrder: 'asc' },
              include: {
                fields: { orderBy: { sortOrder: 'asc' } },
              },
            },
            fields: {
              where: { stepId: null },
              orderBy: { sortOrder: 'asc' },
            },
          },
        })
      }

      if (!form) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Application form not found or not accepting submissions',
        })
      }

      // Check submission window
      const now = new Date()
      const startDate = form.round?.submissionStartDate || form.opensAt
      const endDate = form.round?.submissionEndDate || form.round?.submissionDeadline || form.closesAt

      if (startDate && now < startDate) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Applications are not yet open',
        })
      }

      if (endDate && now > endDate) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Applications have 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: {
          id: form.id,
          name: form.name,
          description: form.description,
          confirmationMessage: form.confirmationMessage,
        },
        program: form.program,
        round: form.round,
        steps: form.steps,
        orphanFields: form.fields, // Fields not assigned to any step
      }
    }),

  /**
   * Submit an application through the onboarding wizard
   * Creates Project, TeamMembers, and sends emails
   */
  submit: publicProcedure
    .input(
      z.object({
        formId: z.string(),
        // Contact info
        contactName: z.string().min(1),
        contactEmail: z.string().email(),
        contactPhone: z.string().optional(),
        // Project info
        projectName: z.string().min(1),
        description: z.string().optional(),
        competitionCategory: z.enum(['STARTUP', 'BUSINESS_CONCEPT']).optional(),
        oceanIssue: z
          .enum([
            'POLLUTION_REDUCTION',
            'CLIMATE_MITIGATION',
            'TECHNOLOGY_INNOVATION',
            'SUSTAINABLE_SHIPPING',
            'BLUE_CARBON',
            'HABITAT_RESTORATION',
            'COMMUNITY_CAPACITY',
            'SUSTAINABLE_FISHING',
            'CONSUMER_AWARENESS',
            'OCEAN_ACIDIFICATION',
            'OTHER',
          ])
          .optional(),
        country: z.string().optional(),
        institution: z.string().optional(),
        teamName: z.string().optional(),
        wantsMentorship: z.boolean().optional(),
        referralSource: z.string().optional(),
        foundedAt: z.string().datetime().optional(),
        // Team members
        teamMembers: z.array(teamMemberInputSchema).optional(),
        // Additional metadata (unmapped fields)
        metadata: z.record(z.unknown()).optional(),
        // GDPR consent
        gdprConsent: z.boolean(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      if (!input.gdprConsent) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'You must accept the terms and conditions to submit',
        })
      }

      // Get form with round info
      const form = await ctx.prisma.applicationForm.findUniqueOrThrow({
        where: { id: input.formId },
        include: {
          round: true,
          program: 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 if we need a round/program for project creation
      const programId = form.round?.programId || form.programId
      if (!programId) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'This form is not linked to a program',
        })
      }

      // Create or find user for contact email
      let contactUser = await ctx.prisma.user.findUnique({
        where: { email: input.contactEmail },
      })

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

      // Create project
      const project = await ctx.prisma.project.create({
        data: {
          programId,
          title: input.projectName,
          description: input.description,
          teamName: input.teamName || input.projectName,
          competitionCategory: input.competitionCategory,
          oceanIssue: input.oceanIssue,
          country: input.country,
          institution: input.institution,
          wantsMentorship: input.wantsMentorship ?? false,
          referralSource: input.referralSource,
          foundedAt: input.foundedAt ? new Date(input.foundedAt) : null,
          submissionSource: 'PUBLIC_FORM',
          submittedByEmail: input.contactEmail,
          submittedByUserId: contactUser.id,
          submittedAt: new Date(),
          metadataJson: input.metadata as Prisma.InputJsonValue ?? {},
        },
      })

      // Create RoundProject entry if form is linked to a round
      if (form.roundId) {
        await ctx.prisma.roundProject.create({
          data: {
            roundId: form.roundId,
            projectId: project.id,
            status: 'SUBMITTED',
          },
        })
      }

      // Create TeamMember for contact as LEAD
      await ctx.prisma.teamMember.create({
        data: {
          projectId: project.id,
          userId: contactUser.id,
          role: 'LEAD',
          title: 'Team Lead',
        },
      })

      // Process additional team members
      const invitePromises: Promise<void>[] = []

      if (input.teamMembers && input.teamMembers.length > 0) {
        for (const member of input.teamMembers) {
          // Skip if same email as contact
          if (member.email === input.contactEmail) continue

          let memberUser = member.email
            ? await ctx.prisma.user.findUnique({ where: { email: member.email } })
            : null

          if (member.email && !memberUser) {
            // Create user with invite token
            const inviteToken = nanoid(32)
            const inviteTokenExpiresAt = new Date()
            inviteTokenExpiresAt.setDate(inviteTokenExpiresAt.getDate() + 30) // 30 days

            memberUser = await ctx.prisma.user.create({
              data: {
                email: member.email,
                name: member.name,
                role: 'APPLICANT',
                status: 'INVITED',
                inviteToken,
                inviteTokenExpiresAt,
              },
            })

            // Queue invite email
            if (form.sendTeamInviteEmails) {
              const inviteUrl = `${process.env.NEXTAUTH_URL || ''}/accept-invite?token=${inviteToken}`
              invitePromises.push(
                sendTeamMemberInviteEmail(
                  member.email,
                  member.name,
                  input.projectName,
                  input.contactName,
                  inviteUrl
                ).catch((err) => {
                  console.error(`Failed to send invite email to ${member.email}:`, err)
                })
              )
            }
          }

          // Create team member if we have a user
          if (memberUser) {
            await ctx.prisma.teamMember.create({
              data: {
                projectId: project.id,
                userId: memberUser.id,
                role: member.role,
                title: member.title,
              },
            })
          }
        }
      }

      // Create form submission record
      await ctx.prisma.applicationFormSubmission.create({
        data: {
          formId: input.formId,
          email: input.contactEmail,
          name: input.contactName,
          dataJson: input as unknown as Prisma.InputJsonValue,
          status: 'SUBMITTED',
        },
      })

      // Send confirmation email
      if (form.sendConfirmationEmail) {
        const programName = form.program?.name || form.round?.name || 'the program'
        try {
          await sendApplicationConfirmationEmail(
            input.contactEmail,
            input.contactName,
            input.projectName,
            programName,
            form.confirmationEmailBody || form.confirmationMessage || undefined
          )
        } catch (err) {
          console.error('Failed to send confirmation email:', err)
        }
      }

      // Wait for invite emails (don't block on failure)
      await Promise.allSettled(invitePromises)

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: contactUser.id,
          action: 'SUBMIT_APPLICATION',
          entityType: 'Project',
          entityId: project.id,
          detailsJson: {
            formId: input.formId,
            projectName: input.projectName,
            teamMemberCount: (input.teamMembers?.length || 0) + 1,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      // In-app notification for applicant
      const programName = form.program?.name || form.round?.name || 'the program'
      await createNotification({
        userId: contactUser.id,
        type: NotificationTypes.APPLICATION_SUBMITTED,
        title: 'Application Received',
        message: `Your application for "${input.projectName}" has been successfully submitted.`,
        linkUrl: `/team/projects/${project.id}`,
        linkLabel: 'View Application',
        metadata: {
          projectName: input.projectName,
          programName,
        },
      })

      // Notify admins of new application
      await notifyAdmins({
        type: NotificationTypes.NEW_APPLICATION,
        title: 'New Application',
        message: `New application received: "${input.projectName}" from ${input.contactName}.`,
        linkUrl: `/admin/projects/${project.id}`,
        linkLabel: 'Review Application',
        metadata: {
          projectName: input.projectName,
          applicantName: input.contactName,
          applicantEmail: input.contactEmail,
          programName,
        },
      })

      return {
        success: true,
        projectId: project.id,
        confirmationMessage: form.confirmationMessage,
      }
    }),
})
