import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { Prisma } from '@prisma/client'
import { router, adminProcedure } from '../trpc'
import {
  testNotionConnection,
  getNotionDatabaseSchema,
  queryNotionDatabase,
} from '@/lib/notion'

export const notionImportRouter = router({
  /**
   * Test connection to Notion API
   */
  testConnection: adminProcedure
    .input(
      z.object({
        apiKey: z.string().min(1),
      })
    )
    .mutation(async ({ input }) => {
      return testNotionConnection(input.apiKey)
    }),

  /**
   * Get database schema (properties) for mapping
   */
  getDatabaseSchema: adminProcedure
    .input(
      z.object({
        apiKey: z.string().min(1),
        databaseId: z.string().min(1),
      })
    )
    .query(async ({ input }) => {
      try {
        return await getNotionDatabaseSchema(input.apiKey, input.databaseId)
      } catch (error) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message:
            error instanceof Error
              ? error.message
              : 'Failed to fetch database schema',
        })
      }
    }),

  /**
   * Preview data from Notion database
   */
  previewData: adminProcedure
    .input(
      z.object({
        apiKey: z.string().min(1),
        databaseId: z.string().min(1),
        limit: z.number().int().min(1).max(10).default(5),
      })
    )
    .query(async ({ input }) => {
      try {
        const records = await queryNotionDatabase(
          input.apiKey,
          input.databaseId,
          input.limit
        )
        return { records, count: records.length }
      } catch (error) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message:
            error instanceof Error
              ? error.message
              : 'Failed to fetch data from Notion',
        })
      }
    }),

  /**
   * Import projects from Notion database
   */
  importProjects: adminProcedure
    .input(
      z.object({
        apiKey: z.string().min(1),
        databaseId: z.string().min(1),
        roundId: z.string(),
        // Column mappings: Notion property name -> Project field
        mappings: z.object({
          title: z.string(), // Required
          teamName: z.string().optional(),
          description: z.string().optional(),
          tags: z.string().optional(), // Multi-select property
        }),
        // Store unmapped columns in metadataJson
        includeUnmappedInMetadata: z.boolean().default(true),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify round exists
      const round = await ctx.prisma.round.findUniqueOrThrow({
        where: { id: input.roundId },
      })

      // Fetch all records from Notion
      const records = await queryNotionDatabase(input.apiKey, input.databaseId)

      if (records.length === 0) {
        return { imported: 0, skipped: 0, errors: [] }
      }

      const results = {
        imported: 0,
        skipped: 0,
        errors: [] as Array<{ recordId: string; error: string }>,
      }

      // Process each record
      for (const record of records) {
        try {
          // Get mapped values
          const title = getPropertyValue(record.properties, input.mappings.title)

          if (!title || typeof title !== 'string' || !title.trim()) {
            results.errors.push({
              recordId: record.id,
              error: 'Missing or invalid title',
            })
            results.skipped++
            continue
          }

          const teamName = input.mappings.teamName
            ? getPropertyValue(record.properties, input.mappings.teamName)
            : null

          const description = input.mappings.description
            ? getPropertyValue(record.properties, input.mappings.description)
            : null

          let tags: string[] = []
          if (input.mappings.tags) {
            const tagsValue = getPropertyValue(record.properties, input.mappings.tags)
            if (Array.isArray(tagsValue)) {
              tags = tagsValue.filter((t): t is string => typeof t === 'string')
            } else if (typeof tagsValue === 'string') {
              tags = tagsValue.split(',').map((t) => t.trim()).filter(Boolean)
            }
          }

          // Build metadata from unmapped columns
          let metadataJson: Record<string, unknown> | null = null
          if (input.includeUnmappedInMetadata) {
            const mappedKeys = new Set([
              input.mappings.title,
              input.mappings.teamName,
              input.mappings.description,
              input.mappings.tags,
            ].filter(Boolean))

            metadataJson = {}
            for (const [key, value] of Object.entries(record.properties)) {
              if (!mappedKeys.has(key) && value !== null && value !== undefined) {
                metadataJson[key] = value
              }
            }

            if (Object.keys(metadataJson).length === 0) {
              metadataJson = null
            }
          }

          // Create project
          await ctx.prisma.project.create({
            data: {
              roundId: round.id,
              status: 'SUBMITTED',
              title: title.trim(),
              teamName: typeof teamName === 'string' ? teamName.trim() : null,
              description: typeof description === 'string' ? description : null,
              tags,
              metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined,
              externalIdsJson: {
                notionPageId: record.id,
                notionDatabaseId: input.databaseId,
              } as Prisma.InputJsonValue,
            },
          })

          results.imported++
        } catch (error) {
          results.errors.push({
            recordId: record.id,
            error: error instanceof Error ? error.message : 'Unknown error',
          })
          results.skipped++
        }
      }

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'IMPORT',
          entityType: 'Project',
          detailsJson: {
            source: 'notion',
            databaseId: input.databaseId,
            roundId: input.roundId,
            imported: results.imported,
            skipped: results.skipped,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return results
    }),
})

/**
 * Helper to get a property value from a record
 */
function getPropertyValue(
  properties: Record<string, unknown>,
  propertyName: string
): unknown {
  return properties[propertyName] ?? null
}
