import { sql } from 'drizzle-orm'
import type { Database, PostgresTransaction } from '@platform-modules/db'
import { MovementNotFoundError } from './errors.js'
import type { InventorySchema } from './schema.js'
import type { Movement, MovementResult } from './types.js'
import { postMovement } from './internal/post-movement.js'
import { firstRows, mapMovement, withInventoryTransaction } from './internal/public-helpers.js'

export interface ReverseMovementInput {
  readonly tenantId: string
  readonly holderRef: string
}

function reversalKindFor(movement: Movement): Movement['kind'] {
  switch (movement.kind) {
    case 'transfer_out':
      return 'transfer_in'
    case 'transfer_in':
      return 'transfer_out'
    case 'count_variance':
      return 'count_variance'
    default:
      return 'adjust'
  }
}

export async function reverseMovement(
  db: Database<InventorySchema> | PostgresTransaction<InventorySchema>,
  input: ReverseMovementInput,
): Promise<MovementResult[]> {
  return withInventoryTransaction(db, async (tx) => {
    const originals = firstRows(await tx.execute(sql`
      SELECT *
      FROM stock_movement
      WHERE tenant_id = ${input.tenantId}::uuid
        AND holder_ref = ${input.holderRef}
      ORDER BY occurred_at ASC, created_at ASC
    `))

    const matching = originals.map(mapMovement)

    if (matching.length === 0) {
      throw new MovementNotFoundError(input.tenantId, input.holderRef)
    }

    const reversedHolderRef = `${input.holderRef}:reversed`
    const existing = await tx.execute(sql`
      SELECT *
      FROM stock_movement
      WHERE tenant_id = ${input.tenantId}::uuid
        AND holder_ref = ${reversedHolderRef}
      ORDER BY occurred_at ASC, created_at ASC
    `)
    const existingRows = firstRows(existing).map(mapMovement)
    if (existingRows.length > 0) {
      return existingRows.map((movement) => ({ movement }))
    }

    const occurredAt = new Date()
    const reversed: MovementResult[] = []
    for (const movement of matching) {
      const qtyDelta = -Number(movement.qtyDelta)
      const unitCost = qtyDelta > 0 ? Number(movement.unitCost ?? 0) : undefined
      reversed.push(
        await postMovement(tx, {
          tenantId: movement.tenantId,
          itemId: movement.itemId,
          locationId: movement.locationId,
          kind: reversalKindFor(movement),
          qtyDelta,
          unitCost,
          holderRef: reversedHolderRef,
          transferGroup: movement.transferGroup ?? undefined,
          occurredAt,
        }),
      )
    }

    return reversed
  })
}
