import { db } from '@sim/db'
import { workflow, workflowDeploymentVersion, workflowSchedule } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { authorizeWorkflowByWorkspacePermission } from '@sim/platform-authz/workflow'
import { and, eq, isNull, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createScheduleContract, scheduleQuerySchema } from '@/lib/api/contracts/schedules'
import { parseRequest, validationErrorResponse } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { performCreateJob } from '@/lib/workflows/schedules/orchestration'
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'

const logger = createLogger('ScheduledAPI')

/**
 * Get schedule information for a workflow, or all schedules for a workspace.
 *
 * Query params (choose one):
 *   - workflowId + optional blockId  → single schedule for one workflow
 *   - workspaceId                    → all schedules across the workspace
 */
export const GET = withRouteHandler(async (req: NextRequest) => {
  const requestId = generateRequestId()
  const url = new URL(req.url)
  const queryValidation = scheduleQuerySchema.safeParse(
    Object.fromEntries(url.searchParams.entries())
  )
  if (!queryValidation.success) return validationErrorResponse(queryValidation.error)
  const { workflowId, workspaceId, blockId } = queryValidation.data

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

    if (workspaceId) {
      return handleWorkspaceSchedules(requestId, session.user.id, workspaceId)
    }

    if (!workflowId) {
      return NextResponse.json(
        { error: 'Missing workflowId or workspaceId parameter' },
        { status: 400 }
      )
    }

    const authorization = await authorizeWorkflowByWorkspacePermission({
      workflowId,
      userId: session.user.id,
      action: 'read',
    })

    if (!authorization.workflow) {
      return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
    }

    if (!authorization.allowed) {
      return NextResponse.json(
        { error: authorization.message || 'Not authorized to view this workflow' },
        { status: authorization.status }
      )
    }

    logger.info(`[${requestId}] Getting schedule for workflow ${workflowId}`)

    const conditions = [eq(workflowSchedule.workflowId, workflowId)]
    if (blockId) {
      conditions.push(eq(workflowSchedule.blockId, blockId))
    }

    const schedule = await db
      .select({ schedule: workflowSchedule })
      .from(workflowSchedule)
      .leftJoin(
        workflowDeploymentVersion,
        and(
          eq(workflowDeploymentVersion.workflowId, workflowSchedule.workflowId),
          eq(workflowDeploymentVersion.isActive, true)
        )
      )
      .where(
        and(
          ...conditions,
          isNull(workflowSchedule.archivedAt),
          or(
            eq(workflowSchedule.deploymentVersionId, workflowDeploymentVersion.id),
            and(isNull(workflowDeploymentVersion.id), isNull(workflowSchedule.deploymentVersionId))
          )
        )
      )
      .limit(1)

    const headers = new Headers()
    headers.set('Cache-Control', 'no-store, max-age=0')

    if (schedule.length === 0) {
      return NextResponse.json({ schedule: null }, { headers })
    }

    const scheduleData = schedule[0].schedule
    const isDisabled = scheduleData.status === 'disabled'
    const hasFailures = scheduleData.failedCount > 0

    return NextResponse.json(
      {
        schedule: scheduleData,
        isDisabled,
        hasFailures,
        canBeReactivated: isDisabled,
      },
      { headers }
    )
  } catch (error) {
    logger.error(`[${requestId}] Error retrieving workflow schedule`, error)
    return NextResponse.json({ error: 'Failed to retrieve workflow schedule' }, { status: 500 })
  }
})

async function handleWorkspaceSchedules(requestId: string, userId: string, workspaceId: string) {
  const hasPermission = await verifyWorkspaceMembership(userId, workspaceId)
  if (!hasPermission) {
    return NextResponse.json({ error: 'Not authorized' }, { status: 403 })
  }

  logger.info(`[${requestId}] Getting all schedules for workspace ${workspaceId}`)

  const [workflowRows, jobRows] = await Promise.all([
    db
      .select({
        schedule: workflowSchedule,
        workflowName: workflow.name,
      })
      .from(workflowSchedule)
      .innerJoin(workflow, eq(workflow.id, workflowSchedule.workflowId))
      .leftJoin(
        workflowDeploymentVersion,
        and(
          eq(workflowDeploymentVersion.workflowId, workflowSchedule.workflowId),
          eq(workflowDeploymentVersion.isActive, true)
        )
      )
      .where(
        and(
          eq(workflow.workspaceId, workspaceId),
          isNull(workflow.archivedAt),
          eq(workflowSchedule.triggerType, 'schedule'),
          isNull(workflowSchedule.archivedAt),
          or(eq(workflowSchedule.sourceType, 'workflow'), isNull(workflowSchedule.sourceType)),
          or(
            eq(workflowSchedule.deploymentVersionId, workflowDeploymentVersion.id),
            and(isNull(workflowDeploymentVersion.id), isNull(workflowSchedule.deploymentVersionId))
          )
        )
      ),
    db
      .select({ schedule: workflowSchedule })
      .from(workflowSchedule)
      .where(
        and(
          eq(workflowSchedule.sourceWorkspaceId, workspaceId),
          eq(workflowSchedule.sourceType, 'job'),
          isNull(workflowSchedule.archivedAt)
        )
      ),
  ])

  const headers = new Headers()
  headers.set('Cache-Control', 'no-store, max-age=0')

  const schedules = [
    ...workflowRows.map((r) => ({
      ...r.schedule,
      workflowName: r.workflowName,
    })),
    ...jobRows.map((r) => ({
      ...r.schedule,
      workflowName: null,
    })),
  ]

  return NextResponse.json({ schedules }, { headers })
}

/**
 * Create a standalone scheduled job.
 *
 * Body: { workspaceId, title, prompt, cronExpression, timezone, lifecycle?, maxRuns?, startDate? }
 */
export const POST = withRouteHandler(async (req: NextRequest) => {
  const requestId = generateRequestId()

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

    const parsed = await parseRequest(
      createScheduleContract,
      req,
      {},
      {
        validationErrorResponse: (error) =>
          NextResponse.json(
            { error: 'Invalid request body', details: error.issues },
            { status: 400 }
          ),
      }
    )
    if (!parsed.success) return parsed.response

    const {
      workspaceId,
      title,
      prompt,
      cronExpression,
      time,
      timezone,
      lifecycle,
      maxRuns,
      endsAt,
      startDate,
      contexts,
    } = parsed.data.body

    const permission = await verifyWorkspaceMembership(session.user.id, workspaceId)
    if (permission !== 'admin' && permission !== 'write') {
      return NextResponse.json({ error: 'Not authorized' }, { status: 403 })
    }

    const result = await performCreateJob({
      workspaceId,
      userId: session.user.id,
      actorName: session.user.name,
      actorEmail: session.user.email,
      title,
      prompt,
      cronExpression,
      time,
      timezone,
      lifecycle,
      maxRuns,
      endsAt,
      startDate,
      contexts,
      request: req,
    })
    if (!result.success || !result.schedule) {
      return NextResponse.json(
        { error: result.error || 'Failed to create schedule' },
        { status: result.errorCode === 'validation' ? 400 : 500 }
      )
    }

    logger.info(`[${requestId}] Created job schedule ${result.schedule.id}`, {
      title,
      cronExpression,
      timezone,
      lifecycle,
    })

    return NextResponse.json(
      {
        schedule: {
          id: result.schedule.id,
          status: result.schedule.status,
          cronExpression: result.schedule.cronExpression,
          nextRunAt: result.schedule.nextRunAt,
        },
      },
      { status: 201 }
    )
  } catch (error) {
    logger.error(`[${requestId}] Error creating schedule`, error)
    return NextResponse.json({ error: 'Failed to create schedule' }, { status: 500 })
  }
})
