import { ReturnTransitionError } from '../errors.js'
import type { ReturnCommand, ReturnEvent, ReturnState, ReturnStatus } from './types.js'

const TERMINAL: ReadonlySet<ReturnStatus> = new Set(['refunded', 'rejected'])

function invalid(state: ReturnState, cmd: ReturnCommand): never {
  throw new ReturnTransitionError({ from: state.status, command: cmd.type })
}

export function decideReturn(state: ReturnState, cmd: ReturnCommand): ReturnEvent[] {
  if (TERMINAL.has(state.status)) {
    invalid(state, cmd)
  }

  switch (state.status) {
    case 'requested':
      if (cmd.type === 'vendor.approve') {
        return [{ type: 'return.approved' }]
      }
      if (cmd.type === 'vendor.reject') {
        return [{ type: 'return.rejected' }]
      }
      if (cmd.type === 'buyer.cancel') {
        return [{ type: 'return.cancelled' }]
      }
      if (cmd.type === 'sla.autoApprove') {
        return [{ type: 'return.approved' }]
      }
      return invalid(state, cmd)

    case 'approved':
      if (cmd.type === 'buyer.cancel') {
        return [{ type: 'return.cancelled' }]
      }
      if (cmd.type === 'admin.override' && cmd.to === 'refunded') {
        return [{ type: 'return.refunded' }]
      }
      if (cmd.type === 'admin.override') {
        return [{ type: 'return.overridden', to: cmd.to }]
      }
      return invalid(state, cmd)

    case 'in_transit':
      if (cmd.type === 'carrier.received') {
        return [{ type: 'return.received' }]
      }
      if (cmd.type === 'admin.override' && cmd.to === 'refunded') {
        return [{ type: 'return.refunded' }]
      }
      if (cmd.type === 'admin.override') {
        return [{ type: 'return.overridden', to: cmd.to }]
      }
      return invalid(state, cmd)

    case 'received':
      if (cmd.type === 'admin.override' && cmd.to === 'refunded') {
        return [{ type: 'return.refunded' }]
      }
      if (cmd.type === 'admin.override') {
        return [{ type: 'return.overridden', to: cmd.to }]
      }
      return invalid(state, cmd)

    default:
      return invalid(state, cmd)
  }
}

/** Test/helper: derive the next status after applying return events. */
export function applyReturnEvents(status: ReturnStatus, events: ReturnEvent[]): ReturnStatus {
  let next = status
  for (const event of events) {
    switch (event.type) {
      case 'return.requested':
        next = 'requested'
        break
      case 'return.approved':
        next = 'approved'
        break
      case 'return.rejected':
      case 'return.cancelled':
        next = 'rejected'
        break
      case 'return.in_transit':
        next = 'in_transit'
        break
      case 'return.received':
        next = 'received'
        break
      case 'return.refunded':
        next = 'refunded'
        break
      case 'return.overridden':
        next = event.to
        break
    }
  }
  return next
}
