import { prisma } from '@/lib/db'
import { AUDIT_ACTIONS, writeAudit } from '@/lib/audit'
import { apiRoute } from '@/lib/api/handler'
import { ApiError, ERROR_CODES } from '@/lib/api/errors'
import {
  recommendationDecisionSchema,
  type RecommendationDecisionInput,
} from '@/lib/api/schemas'

export const dynamic = 'force-dynamic'

const REQUIRED_PERMISSION: Record<string, string> = {
  approve: 'recommendations.approve',
  reject: 'recommendations.approve',
  execute: 'recommendations.execute',
}

const NEXT_STATUS: Record<string, string> = {
  approve: 'approved',
  reject: 'rejected',
  execute: 'executed',
}

/**
 * Record a decision on a recommended action.
 *
 * Two rules are enforced here rather than in the UI:
 *  • an action must have been simulated before it can be approved (§7) — the platform
 *    does not let anyone dispatch grid actions on the strength of a suggestion alone;
 *  • only an approved action can be marked executed, so the record cannot skip a step.
 */
export const PATCH = apiRoute<{ id: string }, RecommendationDecisionInput>(
  {
    permission: ['recommendations.approve', 'recommendations.execute'],
    bodySchema: recommendationDecisionSchema,
  },
  async ({ params, body, user, request }) => {
    const recommendation = await prisma.recommendation.findUnique({
      where: { id: params.id },
      include: { asset: { select: { code: true } } },
    })
    if (!recommendation) throw new ApiError(ERROR_CODES.notFound)

    const needed = REQUIRED_PERMISSION[body.decision]
    if (!user.permissions.has(needed)) throw new ApiError(ERROR_CODES.permissionDenied)

    if (body.decision === 'approve' && recommendation.status !== 'simulated') {
      throw new ApiError(ERROR_CODES.conflict, {
        message: 'This action must be simulated before it can be approved.',
      })
    }
    if (body.decision === 'execute' && recommendation.status !== 'approved') {
      throw new ApiError(ERROR_CODES.conflict, {
        message: 'Only an approved action can be marked as executed.',
      })
    }
    if (['approved', 'executed', 'rejected'].includes(recommendation.status) && body.decision !== 'execute') {
      throw new ApiError(ERROR_CODES.duplicateSubmission)
    }

    const updated = await prisma.recommendation.update({
      where: { id: recommendation.id },
      data: {
        status: NEXT_STATUS[body.decision],
        decidedAt: new Date(),
        decisionNote: body.note ?? null,
        createdById: recommendation.createdById ?? user.id,
      },
    })

    await writeAudit({
      action:
        body.decision === 'approve'
          ? AUDIT_ACTIONS.recommendationApproved
          : body.decision === 'execute'
            ? AUDIT_ACTIONS.recommendationExecuted
            : AUDIT_ACTIONS.recommendationRejected,
      entity: 'recommendation',
      entityId: recommendation.id,
      user,
      request,
      before: { status: recommendation.status },
      after: { status: updated.status, note: body.note ?? null },
      detail: `${recommendation.code} on ${recommendation.asset.code}`,
    })

    return { id: updated.id, status: updated.status, decidedAt: updated.decidedAt }
  },
)

/** Marks a recommendation as simulated once its effect has been measured. */
export const POST = apiRoute<{ id: string }>(
  { permission: 'simulations.run' },
  async ({ params, user, request }) => {
    const recommendation = await prisma.recommendation.findUnique({ where: { id: params.id } })
    if (!recommendation) throw new ApiError(ERROR_CODES.notFound)

    const updated = await prisma.recommendation.update({
      where: { id: recommendation.id },
      data: { status: recommendation.status === 'proposed' ? 'simulated' : recommendation.status },
    })

    await writeAudit({
      action: AUDIT_ACTIONS.simulationStarted,
      entity: 'recommendation',
      entityId: recommendation.id,
      user,
      request,
      before: { status: recommendation.status },
      after: { status: updated.status },
    })

    return { id: updated.id, status: updated.status }
  },
)
