import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { db } from '@sim/db'
import { templateCreators, templates, workflow } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { templateIdParamsSchema, updateTemplateContract } from '@/lib/api/contracts/templates'
import { parseRequest } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { RateLimiter } from '@/lib/core/rate-limiter'
import { generateRequestId, getClientIp } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { canAccessTemplate } from '@/lib/templates/permissions'
import {
  extractRequiredCredentials,
  sanitizeCredentials,
} from '@/lib/workflows/credentials/credential-extractor'
import type { WorkflowState } from '@/stores/workflows/workflow/types'

const logger = createLogger('TemplateByIdAPI')

const viewRateLimiter = new RateLimiter()

/**
 * Per-IP, per-template view-counter dedup bucket: one increment per 10 minutes.
 * Prevents scripted inflation of `templates.views` from the public GET handler.
 */
const TEMPLATE_VIEW_DEDUP = {
  maxTokens: 1,
  refillRate: 1,
  refillIntervalMs: 10 * 60_000,
}

export const revalidate = 0

export const GET = withRouteHandler(
  async (request: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
    const requestId = generateRequestId()
    const { id } = templateIdParamsSchema.parse(await params)

    try {
      const session = await getSession()

      const access = await canAccessTemplate(id, session?.user?.id)
      if (!access.allowed || !access.template) {
        logger.warn(`[${requestId}] Template not found: ${id}`)
        return NextResponse.json({ error: 'Template not found' }, { status: 404 })
      }

      const result = await db
        .select({
          template: templates,
          creator: templateCreators,
        })
        .from(templates)
        .leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
        .where(eq(templates.id, id))
        .limit(1)

      const { template, creator } = result[0]
      const templateWithCreator = {
        ...template,
        creator: creator || undefined,
      }

      let isStarred = false
      if (session?.user?.id) {
        const { templateStars } = await import('@sim/db/schema')
        const starResult = await db
          .select()
          .from(templateStars)
          .where(
            sql`${templateStars.templateId} = ${id} AND ${templateStars.userId} = ${session.user.id}`
          )
          .limit(1)
        isStarred = starResult.length > 0
      }

      let shouldIncrementView = template.status === 'approved'

      if (shouldIncrementView) {
        const viewer = session?.user?.id ?? `ip:${getClientIp(request)}`
        const dedupKey = `template-view:${id}:${viewer}`
        const { allowed } = await viewRateLimiter.checkRateLimitDirect(
          dedupKey,
          TEMPLATE_VIEW_DEDUP
        )
        if (!allowed) {
          shouldIncrementView = false
        } else {
          try {
            await db
              .update(templates)
              .set({
                views: sql`${templates.views} + 1`,
              })
              .where(eq(templates.id, id))
          } catch (viewError) {
            logger.warn(
              `[${requestId}] Failed to increment view count for template: ${id}`,
              viewError
            )
          }
        }
      }

      logger.info(`[${requestId}] Successfully retrieved template: ${id}`)

      return NextResponse.json({
        data: {
          ...templateWithCreator,
          views: template.views + (shouldIncrementView ? 1 : 0),
          isStarred,
        },
      })
    } catch (error) {
      logger.error(`[${requestId}] Error fetching template: ${id}`, error)
      return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
    }
  }
)

// PUT /api/templates/[id] - Update a template
export const PUT = withRouteHandler(
  async (request: NextRequest, context: { params: Promise<{ id: string }> }) => {
    const requestId = generateRequestId()

    try {
      const session = await getSession()
      if (!session?.user?.id) {
        logger.warn(`[${requestId}] Unauthorized template update attempt`)
        return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
      }

      const parsed = await parseRequest(updateTemplateContract, request, context, {
        validationErrorResponse: (error) => {
          logger.warn(`[${requestId}] Invalid template data for update`, error)
          return NextResponse.json(
            { error: 'Invalid template data', details: error.issues },
            { status: 400 }
          )
        },
      })
      if (!parsed.success) return parsed.response

      const { id } = parsed.data.params
      const { name, details, creatorId, tags, updateState, status } = parsed.data.body

      const existingTemplate = await db
        .select()
        .from(templates)
        .where(eq(templates.id, id))
        .limit(1)

      if (existingTemplate.length === 0) {
        logger.warn(`[${requestId}] Template not found for update: ${id}`)
        return NextResponse.json({ error: 'Template not found' }, { status: 404 })
      }

      const template = existingTemplate[0]

      // Status changes require super user permission
      if (status !== undefined) {
        const { verifyEffectiveSuperUser } = await import('@/lib/templates/permissions')
        const { effectiveSuperUser } = await verifyEffectiveSuperUser(session.user.id)
        if (!effectiveSuperUser) {
          logger.warn(`[${requestId}] Non-super user attempted to change template status: ${id}`)
          return NextResponse.json(
            { error: 'Only super users can change template status' },
            { status: 403 }
          )
        }
      }

      // For non-status updates, verify creator permission
      const hasNonStatusUpdates =
        name !== undefined ||
        details !== undefined ||
        creatorId !== undefined ||
        tags !== undefined ||
        updateState

      if (hasNonStatusUpdates) {
        if (!template.creatorId) {
          logger.warn(`[${requestId}] Template ${id} has no creator, denying update`)
          return NextResponse.json({ error: 'Access denied' }, { status: 403 })
        }

        const { verifyCreatorPermission } = await import('@/lib/templates/permissions')
        const { hasPermission, error: permissionError } = await verifyCreatorPermission(
          session.user.id,
          template.creatorId,
          'admin'
        )

        if (!hasPermission) {
          logger.warn(`[${requestId}] User denied permission to update template ${id}`)
          return NextResponse.json({ error: permissionError || 'Access denied' }, { status: 403 })
        }
      }

      const updateData: Record<string, unknown> = {
        updatedAt: new Date(),
      }

      if (name !== undefined) updateData.name = name
      if (details !== undefined) updateData.details = details
      if (tags !== undefined) updateData.tags = tags
      if (creatorId !== undefined) updateData.creatorId = creatorId
      if (status !== undefined) updateData.status = status

      if (updateState && template.workflowId) {
        const { authorizeWorkflowByWorkspacePermission } = await import('@sim/workflow-authz')
        const authorization = await authorizeWorkflowByWorkspacePermission({
          userId: session.user.id,
          workflowId: template.workflowId,
          action: 'read',
        })

        if (!authorization.allowed) {
          logger.warn(`[${requestId}] User denied workflow access for state sync on template ${id}`)
          return NextResponse.json({ error: 'Access denied to workflow' }, { status: 403 })
        }

        const { loadWorkflowFromNormalizedTables } = await import(
          '@/lib/workflows/persistence/utils'
        )
        const normalizedData = await loadWorkflowFromNormalizedTables(template.workflowId)

        if (normalizedData) {
          const [workflowRecord] = await db
            .select({ variables: workflow.variables })
            .from(workflow)
            .where(eq(workflow.id, template.workflowId))
            .limit(1)

          const currentState: Partial<WorkflowState> = {
            blocks: normalizedData.blocks,
            edges: normalizedData.edges,
            loops: normalizedData.loops,
            parallels: normalizedData.parallels,
            variables: (workflowRecord?.variables as WorkflowState['variables']) ?? undefined,
            lastSaved: Date.now(),
          }

          const requiredCredentials = extractRequiredCredentials(currentState)

          const sanitizedState = sanitizeCredentials(currentState)

          updateData.state = sanitizedState
          updateData.requiredCredentials = requiredCredentials

          logger.info(
            `[${requestId}] Updating template state and credentials from current workflow: ${template.workflowId}`
          )
        } else {
          logger.warn(`[${requestId}] Could not load workflow state for template: ${id}`)
        }
      }

      const updatedTemplate = await db
        .update(templates)
        .set(updateData)
        .where(eq(templates.id, id))
        .returning()

      logger.info(`[${requestId}] Successfully updated template: ${id}`)

      recordAudit({
        actorId: session.user.id,
        actorName: session.user.name,
        actorEmail: session.user.email,
        action: AuditAction.TEMPLATE_UPDATED,
        resourceType: AuditResourceType.TEMPLATE,
        resourceId: id,
        resourceName: name ?? template.name,
        description: `Updated template "${name ?? template.name}"`,
        metadata: {
          templateName: name ?? template.name,
          updatedFields: Object.keys(parsed.data.body).filter(
            (k) => parsed.data.body[k as keyof typeof parsed.data.body] !== undefined
          ),
          statusChange: status !== undefined ? { from: template.status, to: status } : undefined,
          stateUpdated: updateState || false,
          workflowId: template.workflowId || undefined,
        },
        request,
      })

      return NextResponse.json({
        data: updatedTemplate[0],
        message: 'Template updated successfully',
      })
    } catch (error) {
      logger.error(`[${requestId}] Error updating template`, error)
      return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
    }
  }
)

// DELETE /api/templates/[id] - Delete a template
export const DELETE = withRouteHandler(
  async (request: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
    const requestId = generateRequestId()
    const { id } = templateIdParamsSchema.parse(await params)

    try {
      const session = await getSession()
      if (!session?.user?.id) {
        logger.warn(`[${requestId}] Unauthorized template delete attempt for ID: ${id}`)
        return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
      }

      const existing = await db
        .select({
          name: templates.name,
          workflowId: templates.workflowId,
          creatorId: templates.creatorId,
          status: templates.status,
          tags: templates.tags,
        })
        .from(templates)
        .where(eq(templates.id, id))
        .limit(1)
      if (existing.length === 0) {
        logger.warn(`[${requestId}] Template not found for delete: ${id}`)
        return NextResponse.json({ error: 'Template not found' }, { status: 404 })
      }

      const template = existing[0]

      if (!template.creatorId) {
        logger.warn(`[${requestId}] Template ${id} has no creator, denying delete`)
        return NextResponse.json({ error: 'Access denied' }, { status: 403 })
      }

      const { verifyCreatorPermission } = await import('@/lib/templates/permissions')
      const { hasPermission, error: permissionError } = await verifyCreatorPermission(
        session.user.id,
        template.creatorId,
        'admin'
      )

      if (!hasPermission) {
        logger.warn(`[${requestId}] User denied permission to delete template ${id}`)
        return NextResponse.json({ error: permissionError || 'Access denied' }, { status: 403 })
      }

      await db.delete(templates).where(eq(templates.id, id))

      logger.info(`[${requestId}] Deleted template: ${id}`)

      recordAudit({
        actorId: session.user.id,
        actorName: session.user.name,
        actorEmail: session.user.email,
        action: AuditAction.TEMPLATE_DELETED,
        resourceType: AuditResourceType.TEMPLATE,
        resourceId: id,
        resourceName: template.name,
        description: `Deleted template "${template.name}"`,
        metadata: {
          templateName: template.name,
          workflowId: template.workflowId || undefined,
          creatorId: template.creatorId || undefined,
          status: template.status,
          tags: template.tags,
        },
        request,
      })

      return NextResponse.json({ success: true })
    } catch (error) {
      logger.error(`[${requestId}] Error deleting template: ${id}`, error)
      return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
    }
  }
)
