import type { Transaction } from "../generated/kysely-tailordb";
import {
  PolicyNotFoundError,
  InvalidStateError,
  NoActivePolicyToReplaceError,
  TargetMismatchError,
  UnauthorizedError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface ReplaceAuditPolicyInput {
  replacementPolicyId: string;
}

/**
 * Function: replaceAuditPolicy
 *
 * Atomically replaces an existing ACTIVE audit policy with a DRAFT replacement.
 * The existing ACTIVE policy is set to INACTIVE and the replacement is set to ACTIVE.
 */
export async function run(db: Transaction, input: ReplaceAuditPolicyInput, ctx: CommandContext) {
  const { replacementPolicyId } = input;

  // 1. Find replacement policy by ID with forUpdate
  const replacement = await db
    .selectFrom("AuditPolicy")
    .selectAll()
    .where("id", "=", replacementPolicyId)
    .forUpdate()
    .executeTakeFirst();

  if (!replacement) {
    return err(new PolicyNotFoundError(replacementPolicyId));
  }

  // Scope authorization checks
  if (ctx.companyId !== undefined) {
    if (ctx.companyId !== null && replacement.companyId !== ctx.companyId) {
      return err(new UnauthorizedError(ctx.actorId));
    }
    if (ctx.companyId === null && replacement.companyId !== null) {
      return err(new UnauthorizedError(ctx.actorId));
    }
  }

  // 2. Not DRAFT → InvalidStateError
  if (replacement.status !== "DRAFT") {
    return err(new InvalidStateError(replacementPolicyId));
  }

  // 3. Find all ACTIVE policies for the same entityName (not filtered by companyId or operationType
  //    so that TARGET_MISMATCH can fire when they differ)
  const activePolicies = await db
    .selectFrom("AuditPolicy")
    .selectAll()
    .where("entityName", "=", replacement.entityName)
    .where("status", "=", "ACTIVE")
    .forUpdate()
    .execute();

  if (activePolicies.length === 0) {
    return err(new NoActivePolicyToReplaceError(replacementPolicyId));
  }

  // 4. Find the ACTIVE policy that matches the replacement's companyId + operationType
  const existingActive = activePolicies.find(
    (p) => p.companyId === replacement.companyId && p.operationType === replacement.operationType,
  );

  if (!existingActive) {
    return err(new TargetMismatchError(replacementPolicyId));
  }

  // 5. Set existing ACTIVE policy to INACTIVE
  const deactivatedPolicy = await db
    .updateTable("AuditPolicy")
    .set({ status: "INACTIVE", updatedAt: new Date() })
    .where("id", "=", existingActive.id)
    .returningAll()
    .executeTakeFirst();

  // 6. Set replacement policy to ACTIVE
  const activatedPolicy = await db
    .updateTable("AuditPolicy")
    .set({ status: "ACTIVE", updatedAt: new Date() })
    .where("id", "=", replacementPolicyId)
    .returningAll()
    .executeTakeFirst();

  return ok({ deactivatedPolicy: deactivatedPolicy!, activatedPolicy: activatedPolicy! });
}
