import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, adminProcedure, protectedProcedure } from '../trpc'
import {
  tagProject,
  batchTagProjects,
  getTagSuggestions,
  addProjectTag,
  removeProjectTag,
} from '../services/ai-tagging'

export const tagRouter = router({
  /**
   * List all expertise tags
   */
  list: protectedProcedure
    .input(
      z.object({
        category: z.string().optional(),
        isActive: z.boolean().optional(),
        includeUsageCount: z.boolean().default(false),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {}

      if (input.category) {
        where.category = input.category
      }
      if (input.isActive !== undefined) {
        where.isActive = input.isActive
      }

      const tags = await ctx.prisma.expertiseTag.findMany({
        where,
        orderBy: [{ category: 'asc' }, { sortOrder: 'asc' }, { name: 'asc' }],
      })

      if (!input.includeUsageCount) {
        return { tags }
      }

      // Count usage for each tag
      const tagsWithCounts = await Promise.all(
        tags.map(async (tag) => {
          const userCount = await ctx.prisma.user.count({
            where: {
              expertiseTags: { has: tag.name },
            },
          })
          const projectCount = await ctx.prisma.project.count({
            where: {
              tags: { has: tag.name },
            },
          })
          return {
            ...tag,
            userCount,
            projectCount,
            totalUsage: userCount + projectCount,
          }
        })
      )

      return { tags: tagsWithCounts }
    }),

  /**
   * Get a single tag by ID
   */
  get: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const tag = await ctx.prisma.expertiseTag.findUniqueOrThrow({
        where: { id: input.id },
      })

      // Get usage counts
      const [userCount, projectCount] = await Promise.all([
        ctx.prisma.user.count({
          where: { expertiseTags: { has: tag.name } },
        }),
        ctx.prisma.project.count({
          where: { tags: { has: tag.name } },
        }),
      ])

      return {
        ...tag,
        userCount,
        projectCount,
      }
    }),

  /**
   * Get all unique categories
   */
  getCategories: protectedProcedure.query(async ({ ctx }) => {
    const tags = await ctx.prisma.expertiseTag.findMany({
      where: { isActive: true },
      select: { category: true },
      distinct: ['category'],
    })

    return tags
      .map((t) => t.category)
      .filter((c): c is string => c !== null)
      .sort()
  }),

  /**
   * Create a new expertise tag (admin only)
   */
  create: adminProcedure
    .input(
      z.object({
        name: z.string().min(1).max(100),
        description: z.string().optional(),
        category: z.string().optional(),
        color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
        sortOrder: z.number().int().default(0),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Check if tag name already exists
      const existing = await ctx.prisma.expertiseTag.findUnique({
        where: { name: input.name },
      })

      if (existing) {
        throw new TRPCError({
          code: 'CONFLICT',
          message: 'A tag with this name already exists',
        })
      }

      const tag = await ctx.prisma.expertiseTag.create({
        data: input,
      })

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

      return tag
    }),

  /**
   * Update an expertise tag (admin only)
   */
  update: adminProcedure
    .input(
      z.object({
        id: z.string(),
        name: z.string().min(1).max(100).optional(),
        description: z.string().optional().nullable(),
        category: z.string().optional().nullable(),
        color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().nullable(),
        isActive: z.boolean().optional(),
        sortOrder: z.number().int().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input

      // If name is being changed, check for conflicts
      if (data.name) {
        const existing = await ctx.prisma.expertiseTag.findFirst({
          where: {
            name: data.name,
            id: { not: id },
          },
        })

        if (existing) {
          throw new TRPCError({
            code: 'CONFLICT',
            message: 'A tag with this name already exists',
          })
        }
      }

      // Get old tag for name change handling
      const oldTag = await ctx.prisma.expertiseTag.findUniqueOrThrow({
        where: { id },
      })

      const tag = await ctx.prisma.expertiseTag.update({
        where: { id },
        data,
      })

      // If name changed, update all users and projects with this tag
      if (data.name && data.name !== oldTag.name) {
        const newName = data.name

        // Update users
        const usersWithTag = await ctx.prisma.user.findMany({
          where: { expertiseTags: { has: oldTag.name } },
          select: { id: true, expertiseTags: true },
        })

        for (const user of usersWithTag) {
          await ctx.prisma.user.update({
            where: { id: user.id },
            data: {
              expertiseTags: user.expertiseTags.map((t) =>
                t === oldTag.name ? newName : t
              ),
            },
          })
        }

        // Update projects
        const projectsWithTag = await ctx.prisma.project.findMany({
          where: { tags: { has: oldTag.name } },
          select: { id: true, tags: true },
        })

        for (const project of projectsWithTag) {
          await ctx.prisma.project.update({
            where: { id: project.id },
            data: {
              tags: project.tags.map((t) =>
                t === oldTag.name ? newName : t
              ),
            },
          })
        }
      }

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

      return tag
    }),

  /**
   * Delete an expertise tag (admin only)
   */
  delete: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const tag = await ctx.prisma.expertiseTag.findUniqueOrThrow({
        where: { id: input.id },
      })

      // Remove tag from all users
      const usersWithTag = await ctx.prisma.user.findMany({
        where: { expertiseTags: { has: tag.name } },
        select: { id: true, expertiseTags: true },
      })

      for (const user of usersWithTag) {
        await ctx.prisma.user.update({
          where: { id: user.id },
          data: {
            expertiseTags: user.expertiseTags.filter((t) => t !== tag.name),
          },
        })
      }

      // Remove tag from all projects
      const projectsWithTag = await ctx.prisma.project.findMany({
        where: { tags: { has: tag.name } },
        select: { id: true, tags: true },
      })

      for (const project of projectsWithTag) {
        await ctx.prisma.project.update({
          where: { id: project.id },
          data: {
            tags: project.tags.filter((t) => t !== tag.name),
          },
        })
      }

      // Delete the tag
      await ctx.prisma.expertiseTag.delete({
        where: { id: input.id },
      })

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

      return tag
    }),

  /**
   * Bulk create tags (admin only)
   */
  bulkCreate: adminProcedure
    .input(
      z.object({
        tags: z.array(
          z.object({
            name: z.string().min(1).max(100),
            description: z.string().optional(),
            category: z.string().optional(),
            color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
          })
        ),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get existing tag names
      const existingTags = await ctx.prisma.expertiseTag.findMany({
        where: { name: { in: input.tags.map((t) => t.name) } },
        select: { name: true },
      })
      const existingNames = new Set(existingTags.map((t) => t.name))

      // Filter out existing tags
      const newTags = input.tags.filter((t) => !existingNames.has(t.name))

      if (newTags.length === 0) {
        return { created: 0, skipped: input.tags.length }
      }

      // Get max sort order
      const maxOrder = await ctx.prisma.expertiseTag.aggregate({
        _max: { sortOrder: true },
      })
      const startOrder = (maxOrder._max.sortOrder || 0) + 1

      const created = await ctx.prisma.expertiseTag.createMany({
        data: newTags.map((t, i) => ({
          ...t,
          sortOrder: startOrder + i,
        })),
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'BULK_CREATE',
          entityType: 'ExpertiseTag',
          detailsJson: { count: created.count, skipped: existingNames.size },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { created: created.count, skipped: existingNames.size }
    }),

  /**
   * Reorder tags (admin only)
   */
  reorder: 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.expertiseTag.update({
            where: { id: item.id },
            data: { sortOrder: item.sortOrder },
          })
        )
      )

      return { success: true }
    }),

  // ═══════════════════════════════════════════════════════════════════════════
  // PROJECT TAGGING (AI-powered)
  // ═══════════════════════════════════════════════════════════════════════════

  /**
   * Get tags for a project
   */
  getProjectTags: protectedProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      const tags = await ctx.prisma.projectTag.findMany({
        where: { projectId: input.projectId },
        include: { tag: true },
        orderBy: { confidence: 'desc' },
      })

      return tags.map((pt) => ({
        id: pt.id,
        tagId: pt.tagId,
        name: pt.tag.name,
        category: pt.tag.category,
        color: pt.tag.color,
        confidence: pt.confidence,
        source: pt.source,
        createdAt: pt.createdAt,
      }))
    }),

  /**
   * Get AI tag suggestions for a project (without applying)
   */
  getSuggestions: adminProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      const suggestions = await getTagSuggestions(input.projectId, ctx.user.id)
      return suggestions
    }),

  /**
   * Tag a single project with AI
   */
  tagProject: adminProcedure
    .input(z.object({ projectId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const result = await tagProject(input.projectId, ctx.user.id)

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'AI_TAG',
          entityType: 'Project',
          entityId: input.projectId,
          detailsJson: {
            applied: result.applied.map((t) => t.tagName),
            tokensUsed: result.tokensUsed,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return result
    }),

  /**
   * Batch tag all untagged projects in a round
   */
  batchTagProjects: adminProcedure
    .input(z.object({ roundId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const result = await batchTagProjects(input.roundId, ctx.user.id)

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'BATCH_AI_TAG',
          entityType: 'Round',
          entityId: input.roundId,
          detailsJson: {
            processed: result.processed,
            failed: result.failed,
            skipped: result.skipped,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return result
    }),

  /**
   * Manually add a tag to a project
   */
  addProjectTag: adminProcedure
    .input(
      z.object({
        projectId: z.string(),
        tagId: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      await addProjectTag(input.projectId, input.tagId)

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

      return { success: true }
    }),

  /**
   * Remove a tag from a project
   */
  removeProjectTag: adminProcedure
    .input(
      z.object({
        projectId: z.string(),
        tagId: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      await removeProjectTag(input.projectId, input.tagId)

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

      return { success: true }
    }),
})
