import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import { approvalRequestLifecycle } from "../db/approvalRequest.lifecycle.generated";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  InvalidStatusTransitionError,
  NotRequesterError,
  RequestNotFoundError,
} from "../lib/errors.generated";

export interface WithdrawApprovalRequestInput {
  approvalRequestId: string;
  comment?: string;
}

/**
 * Function: withdrawApprovalRequest
 *
 * Lets the original requester voluntarily terminate their own request before
 * it resolves. Writes one ApprovalDecision row with decision = WITHDRAW and
 * transitions the request to WITHDRAWN.
 */
export async function run(
  db: Transaction,
  input: WithdrawApprovalRequestInput,
  ctx: CommandContext,
) {
  const request = await db
    .selectFrom("ApprovalRequest")
    .selectAll()
    .where("id", "=", input.approvalRequestId)
    .forUpdate()
    .executeTakeFirst();
  if (!request) return err(new RequestNotFoundError(input.approvalRequestId));

  const nextStatus = approvalRequestLifecycle.tryTransition(request.status, "withdraw");
  if (!nextStatus) return err(new InvalidStatusTransitionError(input.approvalRequestId));

  if (ctx.actorId !== request.requesterId) {
    return err(new NotRequesterError(input.approvalRequestId));
  }

  const now = new Date();
  await db
    .insertInto("ApprovalDecision")
    .values({
      approvalRequestId: request.id,
      decision: "WITHDRAW",
      decidedByUserId: ctx.actorId,
      comment: input.comment,
      decidedAt: now,
    })
    .execute();

  const updated = await db
    .updateTable("ApprovalRequest")
    .set({ status: nextStatus, resolvedAt: now })
    .where("id", "=", input.approvalRequestId)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ approvalRequest: updated });
}
