import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { workflow } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { deleteWorkspaceBodySchema, updateWorkspaceContract } from '@/lib/api/contracts'
import { parseRequest, validationErrorResponse } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { captureServerEvent } from '@/lib/posthog/server'
import { archiveWorkspace } from '@/lib/workspaces/lifecycle'

const logger = createLogger('WorkspaceByIdAPI')

import { db } from '@sim/db'
import { permissions, workspace } from '@sim/db/schema'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import {
  getEffectiveWorkspacePermission,
  getUserEntityPermissions,
} from '@/lib/workspaces/permissions/utils'

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

    if (!session?.user?.id) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
    }

    const workspaceId = id

    // Check if user has any access to this workspace
    const userPermission = await getUserEntityPermissions(session.user.id, 'workspace', workspaceId)
    if (!userPermission) {
      return NextResponse.json({ error: 'Workspace not found or access denied' }, { status: 404 })
    }

    // Get workspace details
    const workspaceDetails = await db
      .select()
      .from(workspace)
      .where(and(eq(workspace.id, workspaceId), isNull(workspace.archivedAt)))
      .then((rows) => rows[0])

    if (!workspaceDetails) {
      return NextResponse.json({ error: 'Workspace not found' }, { status: 404 })
    }

    return NextResponse.json({
      workspace: {
        ...workspaceDetails,
        permissions: userPermission,
      },
    })
  }
)

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

    if (!session?.user?.id) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
    }

    const parsed = await parseRequest(updateWorkspaceContract, request, context)
    if (!parsed.success) return parsed.response

    const workspaceId = parsed.data.params.id

    // Check if user has admin permissions to update workspace
    const userPermission = await getUserEntityPermissions(session.user.id, 'workspace', workspaceId)
    if (userPermission !== 'admin') {
      return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 })
    }

    try {
      const body = parsed.data.body
      const { name, color, logoUrl, billedAccountUserId, allowPersonalApiKeys } = body

      if (
        name === undefined &&
        color === undefined &&
        logoUrl === undefined &&
        billedAccountUserId === undefined &&
        allowPersonalApiKeys === undefined
      ) {
        return NextResponse.json({ error: 'No updates provided' }, { status: 400 })
      }

      const existingWorkspace = await db
        .select()
        .from(workspace)
        .where(and(eq(workspace.id, workspaceId), isNull(workspace.archivedAt)))
        .then((rows) => rows[0])

      if (!existingWorkspace) {
        return NextResponse.json({ error: 'Workspace not found' }, { status: 404 })
      }

      const updateData: Record<string, unknown> = {}

      if (name !== undefined) {
        updateData.name = name
      }

      if (color !== undefined) {
        updateData.color = color
      }

      if (logoUrl !== undefined) {
        updateData.logoUrl = logoUrl
      }

      if (allowPersonalApiKeys !== undefined) {
        updateData.allowPersonalApiKeys = Boolean(allowPersonalApiKeys)
      }

      if (billedAccountUserId !== undefined) {
        if (
          existingWorkspace.organizationId &&
          existingWorkspace.workspaceMode === 'organization'
        ) {
          return NextResponse.json(
            {
              error:
                'Organization workspaces use organization billing and cannot change billed account.',
            },
            { status: 400 }
          )
        }

        if (existingWorkspace.workspaceMode === 'personal') {
          return NextResponse.json(
            {
              error:
                'Personal workspaces are always billed to their owner and cannot change billed account.',
            },
            { status: 400 }
          )
        }

        const candidateId = billedAccountUserId

        const candidatePermission = await getEffectiveWorkspacePermission(
          candidateId,
          existingWorkspace
        )
        if (candidatePermission !== 'admin') {
          return NextResponse.json(
            { error: 'Billed account must be a workspace admin' },
            { status: 400 }
          )
        }

        updateData.billedAccountUserId = candidateId
      }

      if (Object.keys(updateData).length === 0) {
        return NextResponse.json({ error: 'No valid updates provided' }, { status: 400 })
      }

      updateData.updatedAt = new Date()

      await db.update(workspace).set(updateData).where(eq(workspace.id, workspaceId))

      const updatedWorkspace = await db
        .select()
        .from(workspace)
        .where(eq(workspace.id, workspaceId))
        .then((rows) => rows[0])

      recordAudit({
        workspaceId,
        actorId: session.user.id,
        actorName: session.user.name,
        actorEmail: session.user.email,
        action: AuditAction.WORKSPACE_UPDATED,
        resourceType: AuditResourceType.WORKSPACE,
        resourceId: workspaceId,
        resourceName: updatedWorkspace?.name ?? existingWorkspace.name,
        description: `Updated workspace "${updatedWorkspace?.name ?? existingWorkspace.name}"`,
        metadata: {
          changes: {
            ...(name !== undefined && { name: { from: existingWorkspace.name, to: name } }),
            ...(color !== undefined && { color: { from: existingWorkspace.color, to: color } }),
            ...(logoUrl !== undefined && {
              logoUrl: { from: existingWorkspace.logoUrl, to: logoUrl },
            }),
            ...(allowPersonalApiKeys !== undefined && {
              allowPersonalApiKeys: {
                from: existingWorkspace.allowPersonalApiKeys,
                to: allowPersonalApiKeys,
              },
            }),
            ...(billedAccountUserId !== undefined && {
              billedAccountUserId: {
                from: existingWorkspace.billedAccountUserId,
                to: billedAccountUserId,
              },
            }),
          },
        },
        request,
      })

      return NextResponse.json({
        workspace: {
          ...updatedWorkspace,
          permissions: userPermission,
        },
      })
    } catch (error) {
      logger.error('Error updating workspace:', error)
      return NextResponse.json({ error: 'Failed to update workspace' }, { status: 500 })
    }
  }
)

export const DELETE = withRouteHandler(
  async (request: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
    const { id } = await params
    const session = await getSession()

    if (!session?.user?.id) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
    }

    const workspaceId = id
    const rawBody = await request.json().catch(() => ({}))
    const bodyValidation = deleteWorkspaceBodySchema.safeParse(rawBody)
    if (!bodyValidation.success) return validationErrorResponse(bodyValidation.error)

    // Check if user has admin permissions to delete workspace
    const userPermission = await getUserEntityPermissions(session.user.id, 'workspace', workspaceId)
    if (userPermission !== 'admin') {
      return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 })
    }

    try {
      const [[workspaceRecord], totalWorkspaces] = await Promise.all([
        db
          .select({ name: workspace.name })
          .from(workspace)
          .where(and(eq(workspace.id, workspaceId), isNull(workspace.archivedAt)))
          .limit(1),
        db
          .select({ id: permissions.entityId })
          .from(permissions)
          .innerJoin(workspace, eq(permissions.entityId, workspace.id))
          .where(
            and(
              eq(permissions.userId, session.user.id),
              eq(permissions.entityType, 'workspace'),
              isNull(workspace.archivedAt)
            )
          ),
      ])

      /** Counts all workspace memberships (any role), not just admin — prevents the user from reaching a zero-workspace state. */
      if (totalWorkspaces.length <= 1) {
        return NextResponse.json({ error: 'Cannot delete the only workspace' }, { status: 400 })
      }

      logger.info(`Deleting workspace ${workspaceId} for user ${session.user.id}`)

      const workspaceWorkflows = await db
        .select({ id: workflow.id })
        .from(workflow)
        .where(eq(workflow.workspaceId, workspaceId))

      const workflowIds = workspaceWorkflows.map((entry) => entry.id)

      const archiveResult = await archiveWorkspace(workspaceId, {
        requestId: `workspace-${workspaceId}`,
      })

      if (!archiveResult.archived && !workspaceRecord) {
        return NextResponse.json({ error: 'Workspace not found' }, { status: 404 })
      }

      recordAudit({
        workspaceId,
        actorId: session.user.id,
        actorName: session.user.name,
        actorEmail: session.user.email,
        action: AuditAction.WORKSPACE_DELETED,
        resourceType: AuditResourceType.WORKSPACE,
        resourceId: workspaceId,
        resourceName: workspaceRecord?.name,
        description: `Archived workspace "${workspaceRecord?.name || workspaceId}"`,
        metadata: {
          affected: {
            workflows: workflowIds.length,
          },
          archived: archiveResult.archived,
        },
        request,
      })

      captureServerEvent(
        session.user.id,
        'workspace_deleted',
        { workspace_id: workspaceId, workflow_count: workflowIds.length },
        { groups: { workspace: workspaceId } }
      )

      return NextResponse.json({ success: true })
    } catch (error) {
      logger.error(`Error deleting workspace ${workspaceId}:`, error)
      return NextResponse.json({ error: 'Failed to delete workspace' }, { status: 500 })
    }
  }
)

export const PUT = withRouteHandler(
  async (request: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
    // Reuse the PATCH handler implementation for PUT requests
    return PATCH(request, { params })
  }
)
