import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { db, workflow } from '@sim/db'
import { createLogger } from '@sim/logger'
import { assertWorkflowMutable, WorkflowLockedError } from '@sim/platform-authz/workflow'
import { getErrorMessage } from '@sim/utils/errors'
import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { updatePublicApiContract } from '@/lib/api/contracts/deployments'
import { parseRequest } from '@/lib/api/server'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { captureServerEvent } from '@/lib/posthog/server'
import { performFullDeploy, performFullUndeploy } from '@/lib/workflows/orchestration'
import { statusForOrchestrationError } from '@/lib/workflows/orchestration/types'
import { validateWorkflowPermissions } from '@/lib/workflows/utils'
import {
  checkNeedsRedeployment,
  createErrorResponse,
  createSuccessResponse,
} from '@/app/api/workflows/utils'
import {
  PublicApiNotAllowedError,
  validatePublicApiAllowed,
} from '@/ee/access-control/utils/permission-check'

const logger = createLogger('WorkflowDeployAPI')

export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'
export const maxDuration = 120

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

    try {
      const { error, workflow: workflowData } = await validateWorkflowPermissions(
        id,
        requestId,
        'read'
      )
      if (error) {
        return createErrorResponse(error.message, error.status)
      }

      if (!workflowData.isDeployed) {
        logger.info(`[${requestId}] Workflow is not deployed: ${id}`)
        return createSuccessResponse({
          isDeployed: false,
          deployedAt: null,
          apiKey: null,
          needsRedeployment: false,
          isPublicApi: workflowData.isPublicApi ?? false,
        })
      }

      const needsRedeployment = await checkNeedsRedeployment(id)

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

      const responseApiKeyInfo = workflowData.workspaceId
        ? 'Workspace API keys'
        : 'Personal API keys'

      return createSuccessResponse({
        apiKey: responseApiKeyInfo,
        isDeployed: workflowData.isDeployed,
        deployedAt: workflowData.deployedAt,
        needsRedeployment,
        isPublicApi: workflowData.isPublicApi ?? false,
      })
    } catch (error: any) {
      logger.error(`[${requestId}] Error fetching deployment info: ${id}`, error)
      return createErrorResponse(error.message || 'Failed to fetch deployment information', 500)
    }
  }
)

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

    try {
      const {
        error,
        session,
        workflow: workflowData,
      } = await validateWorkflowPermissions(id, requestId, 'admin')
      if (error) {
        return createErrorResponse(error.message, error.status)
      }

      const actorUserId: string | null = session?.user?.id ?? null
      if (!actorUserId) {
        logger.warn(`[${requestId}] Unable to resolve actor user for workflow deployment: ${id}`)
        return createErrorResponse('Unable to determine deploying user', 400)
      }
      await assertWorkflowMutable(id)

      const result = await performFullDeploy({
        workflowId: id,
        userId: actorUserId,
        workflowName: workflowData!.name || undefined,
        requestId,
        request,
      })

      if (!result.success) {
        return createErrorResponse(
          result.error || 'Failed to deploy workflow',
          statusForOrchestrationError(result.errorCode)
        )
      }

      logger.info(`[${requestId}] Workflow deployed successfully: ${id}`)

      captureServerEvent(
        actorUserId,
        'workflow_deployed',
        { workflow_id: id, workspace_id: workflowData!.workspaceId ?? '' },
        {
          groups: workflowData!.workspaceId ? { workspace: workflowData!.workspaceId } : undefined,
          setOnce: { first_workflow_deployed_at: new Date().toISOString() },
        }
      )

      const responseApiKeyInfo = workflowData!.workspaceId
        ? 'Workspace API keys'
        : 'Personal API keys'

      return createSuccessResponse({
        apiKey: responseApiKeyInfo,
        isDeployed: true,
        deployedAt: result.deployedAt,
        warnings: result.warnings,
      })
    } catch (error: unknown) {
      if (error instanceof WorkflowLockedError) {
        return createErrorResponse(error.message, error.status)
      }
      const message = getErrorMessage(error, 'Failed to deploy workflow')
      logger.error(`[${requestId}] Error deploying workflow: ${id}`, { error })
      return createErrorResponse(message, 500)
    }
  }
)

export const PATCH = withRouteHandler(
  async (request: NextRequest, context: { params: Promise<{ id: string }> }) => {
    const requestId = generateRequestId()

    try {
      const parsed = await parseRequest(updatePublicApiContract, request, context, {
        validationErrorResponse: () =>
          createErrorResponse('Invalid request body: isPublicApi must be a boolean', 400),
      })
      if (!parsed.success) return parsed.response

      const { id } = parsed.data.params
      const { isPublicApi } = parsed.data.body

      const {
        error,
        session,
        workflow: workflowData,
      } = await validateWorkflowPermissions(id, requestId, 'admin')
      if (error) {
        return createErrorResponse(error.message, error.status)
      }
      await assertWorkflowMutable(id)

      if (isPublicApi) {
        try {
          await validatePublicApiAllowed(session?.user?.id, workflowData?.workspaceId ?? undefined)
        } catch (err) {
          if (err instanceof PublicApiNotAllowedError) {
            return createErrorResponse('Public API access is disabled', 403)
          }
          throw err
        }
      }

      await db.update(workflow).set({ isPublicApi }).where(eq(workflow.id, id))

      logger.info(`[${requestId}] Updated isPublicApi for workflow ${id} to ${isPublicApi}`)

      const wsId = workflowData?.workspaceId

      recordAudit({
        workspaceId: wsId ?? null,
        actorId: session!.user.id,
        action: AuditAction.WORKFLOW_PUBLIC_API_TOGGLED,
        resourceType: AuditResourceType.WORKFLOW,
        resourceId: id,
        resourceName: workflowData?.name ?? undefined,
        description: `${isPublicApi ? 'Enabled' : 'Disabled'} public API for workflow "${workflowData?.name ?? id}"`,
        metadata: { isPublicApi },
        request,
      })

      captureServerEvent(
        session!.user.id,
        'workflow_public_api_toggled',
        { workflow_id: id, workspace_id: wsId ?? '', is_public: isPublicApi },
        wsId ? { groups: { workspace: wsId } } : undefined
      )

      return createSuccessResponse({ isPublicApi })
    } catch (error: unknown) {
      if (error instanceof WorkflowLockedError) {
        return createErrorResponse(error.message, error.status)
      }
      const message = getErrorMessage(error, 'Failed to update deployment settings')
      logger.error(`[${requestId}] Error updating deployment settings`, { error })
      return createErrorResponse(message, 500)
    }
  }
)

export const DELETE = withRouteHandler(
  async (_request: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
    const requestId = generateRequestId()
    const { id } = await params

    try {
      const {
        error,
        session,
        workflow: workflowData,
      } = await validateWorkflowPermissions(id, requestId, 'admin')
      if (error) {
        return createErrorResponse(error.message, error.status)
      }
      await assertWorkflowMutable(id)

      const result = await performFullUndeploy({
        workflowId: id,
        userId: session!.user.id,
        requestId,
      })

      if (!result.success) {
        return createErrorResponse(result.error || 'Failed to undeploy workflow', 500)
      }

      const wsId = workflowData?.workspaceId
      captureServerEvent(
        session!.user.id,
        'workflow_undeployed',
        { workflow_id: id, workspace_id: wsId ?? '' },
        wsId ? { groups: { workspace: wsId } } : undefined
      )

      return createSuccessResponse({
        isDeployed: false,
        deployedAt: null,
        apiKey: null,
        warnings: result.warnings,
      })
    } catch (error: unknown) {
      if (error instanceof WorkflowLockedError) {
        return createErrorResponse(error.message, error.status)
      }
      const message = getErrorMessage(error, 'Failed to undeploy workflow')
      logger.error(`[${requestId}] Error undeploying workflow: ${id}`, { error })
      return createErrorResponse(message, 500)
    }
  }
)
