import {
  insert as insertRequest, findPending, findCancelled, findById, markApproved, markCancelled,
} from '../repositories/orderApprovalRequest.repo.js';
import { createWithId } from '../utils/id.js';
import { NotFoundError, ConflictError } from '../utils/errors.js';
import { emitter } from '../events/emitter.js';
import { create as createOrder } from './order.service.js';
import type { Order } from '../types/index.js';

// The request body is validated against createOrderSchema at the route
// level (POST /api/orders/approval-requests uses the same schema object as
// POST /api/orders) — by the time this runs, `payload` is already a
// shape-valid order body.
export async function submitForApproval(payload: Record<string, unknown>, requestedByEmployeeId: string): Promise<{ id: string }> {
  const { id } = await createWithId('order_approval_requests', 'OAR', (id) => insertRequest(id, payload, requestedByEmployeeId, requestedByEmployeeId));

  emitter.emit('activity.create', {
    entity_type: 'order_approval_request', entity_id: id, action: 'submitted', user_id: requestedByEmployeeId,
  });

  return { id };
}

export async function listPending(filters: { employeeId?: string; dateFrom?: string; dateTo?: string } = {}) {
  return findPending(filters);
}

export async function listCancelled(filters: { employeeId?: string; dateFrom?: string; dateTo?: string } = {}) {
  return findCancelled(filters);
}

// Replays the stored payload through order.service.ts's own create() (not
// duplicated here) so an approved request goes through exactly the same
// order/order_products insert logic as a direct create — the only
// difference is the approval metadata stamped onto the resulting order.
// create() manages its own transaction for the order/order_products insert;
// marking the request 'approved' is a separate, immediately-following write
// (if it somehow failed, the order would already exist with the request
// still 'pending' — reviewable/retryable manually, and preferable to
// silently losing a resulting order that already exists).
export async function approve(requestId: string, reviewerId: string): Promise<Order> {
  const request = await findById(requestId);
  if (!request) throw new NotFoundError('Approval request not found');
  if (request.status !== 'pending') throw new ConflictError('This request has already been reviewed');

  const order = await createOrder(request.payload as Partial<Order>, reviewerId, {
    requestedByEmployeeId: request.requested_by_employee_id,
    approvedByEmployeeId: reviewerId,
  });

  await markApproved(requestId, order.id, reviewerId);

  emitter.emit('activity.create', {
    entity_type: 'order_approval_request', entity_id: requestId, action: 'approved',
    new_values: { resulting_order_id: order.id }, user_id: reviewerId,
  });
  return order;
}

// No row is ever written to `orders` — the pending request is just marked
// cancelled and stays around (status='cancelled') for audit purposes.
export async function cancel(requestId: string, reviewerId: string): Promise<void> {
  const request = await findById(requestId);
  if (!request) throw new NotFoundError('Approval request not found');
  if (request.status !== 'pending') throw new ConflictError('This request has already been reviewed');

  await markCancelled(requestId, reviewerId);
  emitter.emit('activity.create', {
    entity_type: 'order_approval_request', entity_id: requestId, action: 'cancelled', user_id: reviewerId,
  });
}
