import { db } from '@sim/db'
import { permissions, workflow, workflowFolder } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, asc, eq, inArray, isNull, min, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { captureServerEvent } from '@/lib/posthog/server'
import { getNextWorkflowColor } from '@/lib/workflows/colors'
import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults'
import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils'
import { deduplicateWorkflowName, listWorkflows, type WorkflowScope } from '@/lib/workflows/utils'
import { getUserEntityPermissions, workspaceExists } from '@/lib/workspaces/permissions/utils'
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'

const logger = createLogger('WorkflowAPI')

const CreateWorkflowSchema = z.object({
  id: z.string().uuid().optional(),
  name: z.string().min(1, 'Name is required'),
  description: z.string().optional().default(''),
  color: z
    .string()
    .optional()
    .transform((c) => c || getNextWorkflowColor()),
  workspaceId: z.string().optional(),
  folderId: z.string().nullable().optional(),
  sortOrder: z.number().int().optional(),
  deduplicate: z.boolean().optional(),
})

// GET /api/workflows - Get workflows for user (optionally filtered by workspaceId)
export async function GET(request: NextRequest) {
  const requestId = generateRequestId()
  const startTime = Date.now()
  const url = new URL(request.url)
  const workspaceId = url.searchParams.get('workspaceId')
  const scope = (url.searchParams.get('scope') ?? 'active') as WorkflowScope

  try {
    const auth = await checkSessionOrInternalAuth(request, { requireWorkflowId: false })
    if (!auth.success || !auth.userId) {
      logger.warn(`[${requestId}] Unauthorized workflow access attempt`)
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
    }
    const userId = auth.userId

    if (workspaceId) {
      const wsExists = await workspaceExists(workspaceId)

      if (!wsExists) {
        logger.warn(
          `[${requestId}] Attempt to fetch workflows for non-existent workspace: ${workspaceId}`
        )
        return NextResponse.json(
          { error: 'Workspace not found', code: 'WORKSPACE_NOT_FOUND' },
          { status: 404 }
        )
      }

      const userRole = await verifyWorkspaceMembership(userId, workspaceId)

      if (!userRole) {
        logger.warn(
          `[${requestId}] User ${userId} attempted to access workspace ${workspaceId} without membership`
        )
        return NextResponse.json(
          { error: 'Access denied to this workspace', code: 'WORKSPACE_ACCESS_DENIED' },
          { status: 403 }
        )
      }
    }

    if (!['active', 'archived', 'all'].includes(scope)) {
      return NextResponse.json({ error: 'Invalid scope' }, { status: 400 })
    }

    let workflows

    const orderByClause = [asc(workflow.sortOrder), asc(workflow.createdAt), asc(workflow.id)]

    if (workspaceId) {
      workflows = await listWorkflows(workspaceId, { scope })
    } else {
      const workspacePermissionRows = await db
        .select({ workspaceId: permissions.entityId })
        .from(permissions)
        .where(and(eq(permissions.userId, userId), eq(permissions.entityType, 'workspace')))
      const workspaceIds = workspacePermissionRows.map((row) => row.workspaceId)
      if (workspaceIds.length === 0) {
        return NextResponse.json({ data: [] }, { status: 200 })
      }
      workflows = await db
        .select()
        .from(workflow)
        .where(
          scope === 'all'
            ? inArray(workflow.workspaceId, workspaceIds)
            : scope === 'archived'
              ? and(
                  inArray(workflow.workspaceId, workspaceIds),
                  sql`${workflow.archivedAt} IS NOT NULL`
                )
              : and(inArray(workflow.workspaceId, workspaceIds), isNull(workflow.archivedAt))
        )
        .orderBy(...orderByClause)
    }

    return NextResponse.json({ data: workflows }, { status: 200 })
  } catch (error: any) {
    const elapsed = Date.now() - startTime
    logger.error(`[${requestId}] Workflow fetch error after ${elapsed}ms`, error)
    return NextResponse.json({ error: error.message }, { status: 500 })
  }
}

// POST /api/workflows - Create a new workflow
export async function POST(req: NextRequest) {
  const requestId = generateRequestId()
  const auth = await checkSessionOrInternalAuth(req, { requireWorkflowId: false })
  if (!auth.success || !auth.userId) {
    logger.warn(`[${requestId}] Unauthorized workflow creation attempt`)
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  const userId = auth.userId

  try {
    const body = await req.json()
    const {
      id: clientId,
      name: requestedName,
      description,
      color,
      workspaceId,
      folderId,
      sortOrder: providedSortOrder,
      deduplicate,
    } = CreateWorkflowSchema.parse(body)

    if (!workspaceId) {
      logger.warn(`[${requestId}] Workflow creation blocked: missing workspaceId`)
      return NextResponse.json(
        {
          error:
            'workspaceId is required. Personal workflows are deprecated and cannot be created.',
        },
        { status: 400 }
      )
    }

    const workspacePermission = await getUserEntityPermissions(userId, 'workspace', workspaceId)

    if (!workspacePermission || workspacePermission === 'read') {
      logger.warn(
        `[${requestId}] User ${userId} attempted to create workflow in workspace ${workspaceId} without write permissions`
      )
      return NextResponse.json(
        { error: 'Write or Admin access required to create workflows in this workspace' },
        { status: 403 }
      )
    }

    const workflowId = clientId || generateId()
    const now = new Date()

    logger.info(`[${requestId}] Creating workflow ${workflowId} for user ${userId}`)

    let sortOrder: number
    if (providedSortOrder !== undefined) {
      sortOrder = providedSortOrder
    } else {
      const workflowParentCondition = folderId
        ? eq(workflow.folderId, folderId)
        : isNull(workflow.folderId)
      const folderParentCondition = folderId
        ? eq(workflowFolder.parentId, folderId)
        : isNull(workflowFolder.parentId)

      const [[workflowMinResult], [folderMinResult]] = await Promise.all([
        db
          .select({ minOrder: min(workflow.sortOrder) })
          .from(workflow)
          .where(
            and(
              eq(workflow.workspaceId, workspaceId),
              workflowParentCondition,
              isNull(workflow.archivedAt)
            )
          ),
        db
          .select({ minOrder: min(workflowFolder.sortOrder) })
          .from(workflowFolder)
          .where(and(eq(workflowFolder.workspaceId, workspaceId), folderParentCondition)),
      ])

      const minSortOrder = [workflowMinResult?.minOrder, folderMinResult?.minOrder].reduce<
        number | null
      >((currentMin, candidate) => {
        if (candidate == null) return currentMin
        if (currentMin == null) return candidate
        return Math.min(currentMin, candidate)
      }, null)

      sortOrder = minSortOrder != null ? minSortOrder - 1 : 0
    }

    let name = requestedName

    if (deduplicate) {
      name = await deduplicateWorkflowName(requestedName, workspaceId, folderId)
    } else {
      const duplicateConditions = [
        eq(workflow.workspaceId, workspaceId),
        isNull(workflow.archivedAt),
        eq(workflow.name, requestedName),
      ]

      if (folderId) {
        duplicateConditions.push(eq(workflow.folderId, folderId))
      } else {
        duplicateConditions.push(isNull(workflow.folderId))
      }

      const [duplicateWorkflow] = await db
        .select({ id: workflow.id })
        .from(workflow)
        .where(and(...duplicateConditions))
        .limit(1)

      if (duplicateWorkflow) {
        return NextResponse.json(
          { error: `A workflow named "${requestedName}" already exists in this folder` },
          { status: 409 }
        )
      }
    }

    import('@/lib/core/telemetry')
      .then(({ PlatformEvents }) => {
        PlatformEvents.workflowCreated({
          workflowId,
          name,
          workspaceId: workspaceId || undefined,
          folderId: folderId || undefined,
        })
      })
      .catch(() => {
        // Silently fail
      })

    const { workflowState, subBlockValues, startBlockId } = buildDefaultWorkflowArtifacts()

    await db.transaction(async (tx) => {
      await tx.insert(workflow).values({
        id: workflowId,
        userId,
        workspaceId,
        folderId: folderId || null,
        sortOrder,
        name,
        description,
        color,
        lastSynced: now,
        createdAt: now,
        updatedAt: now,
        isDeployed: false,
        runCount: 0,
        variables: {},
      })

      await saveWorkflowToNormalizedTables(workflowId, workflowState, tx)
    })

    logger.info(`[${requestId}] Successfully created workflow ${workflowId} with default blocks`)

    captureServerEvent(
      userId,
      'workflow_created',
      { workflow_id: workflowId, workspace_id: workspaceId ?? '', name },
      {
        groups: workspaceId ? { workspace: workspaceId } : undefined,
        setOnce: { first_workflow_created_at: new Date().toISOString() },
      }
    )

    recordAudit({
      workspaceId,
      actorId: userId,
      actorName: auth.userName,
      actorEmail: auth.userEmail,
      action: AuditAction.WORKFLOW_CREATED,
      resourceType: AuditResourceType.WORKFLOW,
      resourceId: workflowId,
      resourceName: name,
      description: `Created workflow "${name}"`,
      metadata: { name },
      request: req,
    })

    return NextResponse.json({
      id: workflowId,
      name,
      description,
      color,
      workspaceId,
      folderId,
      sortOrder,
      createdAt: now,
      updatedAt: now,
      startBlockId,
      subBlockValues,
    })
  } catch (error) {
    if (error instanceof z.ZodError) {
      logger.warn(`[${requestId}] Invalid workflow creation data`, {
        errors: error.errors,
      })
      return NextResponse.json(
        { error: 'Invalid request data', details: error.errors },
        { status: 400 }
      )
    }

    logger.error(`[${requestId}] Error creating workflow`, error)
    return NextResponse.json({ error: 'Failed to create workflow' }, { status: 500 })
  }
}
