import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CircularDelegationError,
  NotActiveAssigneeError,
  ParentRequestNotPendingError,
  SelfDecisionNotAllowedError,
  SelfDelegationNotAllowedError,
} from "../lib/errors.generated";

export interface DelegateApprovalStepInput {
  approvalStepAssigneeId: string;
  delegatedToUserId: string;
  comment?: string;
}

export async function run(db: Transaction, input: DelegateApprovalStepInput, ctx: CommandContext) {
  const assignee = await db
    .selectFrom("ApprovalStepAssignee")
    .selectAll()
    .where("id", "=", input.approvalStepAssigneeId)
    .forUpdate()
    .executeTakeFirst();
  if (!assignee || assignee.userId !== ctx.actorId || assignee.status !== "PENDING") {
    return err(new NotActiveAssigneeError(input.approvalStepAssigneeId));
  }

  const step = await db
    .selectFrom("ApprovalStep")
    .selectAll()
    .where("id", "=", assignee.approvalStepId)
    .forUpdate()
    .executeTakeFirst();
  if (!step || step.status !== "IN_PROGRESS") {
    return err(new NotActiveAssigneeError(input.approvalStepAssigneeId));
  }

  const request = await db
    .selectFrom("ApprovalRequest")
    .selectAll()
    .where("id", "=", step.approvalRequestId)
    .forUpdate()
    .executeTakeFirst();
  if (!request) {
    return err(new NotActiveAssigneeError(input.approvalStepAssigneeId));
  }
  if (request.status !== "PENDING") {
    return err(new ParentRequestNotPendingError(request.id));
  }

  if (ctx.actorId === request.requesterId) {
    return err(new SelfDecisionNotAllowedError(request.id));
  }
  if (input.delegatedToUserId === ctx.actorId) {
    return err(new SelfDelegationNotAllowedError(input.delegatedToUserId));
  }

  // Circular delegation guard: the prospective delegate must not already hold a
  // PENDING seat on this same step.
  const existingDelegate = await db
    .selectFrom("ApprovalStepAssignee")
    .selectAll()
    .where("approvalStepId", "=", step.id)
    .where("userId", "=", input.delegatedToUserId)
    .where("status", "=", "PENDING")
    .executeTakeFirst();
  if (existingDelegate) {
    return err(new CircularDelegationError(input.delegatedToUserId));
  }

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

  await db
    .updateTable("ApprovalStepAssignee")
    .set({ status: "DELEGATED", resolvedAt: now })
    .where("id", "=", assignee.id)
    .execute();

  await db
    .insertInto("ApprovalStepAssignee")
    .values({
      approvalStepId: step.id,
      userId: input.delegatedToUserId,
      roleId: assignee.roleId,
      required: assignee.required,
      roleQuorum: assignee.roleQuorum,
      status: "PENDING",
    })
    .execute();

  return ok({ approvalRequest: request });
}
