const db = require('../db/models');
const buyerPortalDemoSeeder = require('../db/seeders/20260507130000-buyer-portal-demo-data');

const { Op } = db.Sequelize;

let buyerPortalSeedPromise = null;

const ACTIVE_PRODUCT_STATUSES = ['active', 'seasonal', 'out_of_stock'];

function toNumber(value) {
  if (value === null || value === undefined || value === '') {
    return null;
  }

  const parsed = Number(value);
  return Number.isNaN(parsed) ? null : parsed;
}

function makeError(message) {
  const error = new Error(message);
  error.code = 400;
  error.status = 400;
  error.statusCode = 400;
  return error;
}

const BUYER_ROLE_NAMES = ['Customer Buyer Admin', 'Customer Buyer'];

function isCustomerBuyer(currentUser) {
  return currentUser?.app_role?.name === 'Customer Buyer';
}

function isCustomerBuyerAdmin(currentUser) {
  return currentUser?.app_role?.name === 'Customer Buyer Admin';
}

function isBuyerRole(currentUser) {
  return BUYER_ROLE_NAMES.includes(currentUser?.app_role?.name);
}

async function getBuyerAccountIds(currentUser) {
  if (!isBuyerRole(currentUser)) {
    return null;
  }

  if (!currentUser.email) {
    return [];
  }

  const contacts = await db.contacts.findAll({
    where: {
      email: {
        [Op.iLike]: currentUser.email,
      },
      is_active: true,
    },
  });

  return Array.from(
    new Set(
      contacts
        .map((contact) => contact.accountId)
        .filter((accountId) => Boolean(accountId)),
    ),
  );
}

async function assertAccountAccess(accountId, currentUser) {
  const buyerAccountIds = await getBuyerAccountIds(currentUser);

  if (buyerAccountIds === null) {
    return;
  }

  if (!buyerAccountIds.includes(accountId)) {
    throw makeError(
      'Your buyer account is not allowed to access the selected customer account.',
    );
  }
}

function isCurrent(start, end) {
  const now = new Date();

  if (start && new Date(start) > now) {
    return false;
  }

  if (end && new Date(end) < now) {
    return false;
  }

  return true;
}

function buildOrderNumber() {
  const now = new Date();
  const stamp = [
    now.getUTCFullYear(),
    String(now.getUTCMonth() + 1).padStart(2, '0'),
    String(now.getUTCDate()).padStart(2, '0'),
  ].join('');
  const suffix = String(Math.floor(1000 + Math.random() * 9000));

  return `ORD-${stamp}-${suffix}`;
}

function buildSampleRequestNumber() {
  const now = new Date();
  const stamp = [
    now.getUTCFullYear(),
    String(now.getUTCMonth() + 1).padStart(2, '0'),
    String(now.getUTCDate()).padStart(2, '0'),
  ].join('');
  const suffix = String(Math.floor(1000 + Math.random() * 9000));

  return `SR-${stamp}-${suffix}`;
}

function serializeUser(user) {
  if (!user) {
    return null;
  }

  return {
    id: user.id,
    firstName: user.firstName,
    lastName: user.lastName,
    email: user.email,
    phoneNumber: user.phoneNumber,
  };
}

function serializeContact(contact) {
  if (!contact) {
    return null;
  }

  return {
    id: contact.id,
    accountId: contact.accountId,
    locationId: contact.locationId,
    contact_name: contact.contact_name,
    email: contact.email,
    phone: contact.phone,
    role_title: contact.role_title,
    is_primary: Boolean(contact.is_primary),
    can_place_orders: Boolean(contact.can_place_orders),
    can_see_invoices: Boolean(contact.can_see_invoices),
    is_active: Boolean(contact.is_active),
  };
}

function serializeLocation(location) {
  if (!location) {
    return null;
  }

  return {
    id: location.id,
    accountId: location.accountId,
    location_name: location.location_name,
    location_type: location.location_type,
    address_line_1: location.address_line_1,
    address_line_2: location.address_line_2,
    city: location.city,
    state: location.state,
    postal_code: location.postal_code,
    country: location.country,
    delivery_instructions: location.delivery_instructions,
    is_active: Boolean(location.is_active),
    default_contact: serializeContact(location.default_contact),
  };
}

function serializeProductSummary(product) {
  if (!product) {
    return null;
  }

  return {
    id: product.id,
    sku: product.sku,
    product_name: product.product_name,
    brand: product.brand,
    pack_size: product.pack_size,
    units_per_case: product.units_per_case,
    moq_cases: product.moq_cases,
    temperature_zone: product.temperature_zone,
    product_status: product.product_status,
  };
}

function serializeAccount(account, locations, contacts) {
  return {
    id: account.id,
    account_name: account.account_name,
    account_type: account.account_type,
    account_number: account.account_number,
    credit_status: account.credit_status,
    credit_limit: toNumber(account.credit_limit),
    notes: account.notes,
    default_price_list: account.default_price_list
      ? {
          id: account.default_price_list.id,
          price_list_name: account.default_price_list.price_list_name,
        }
      : null,
    assigned_sales_rep: serializeUser(account.assigned_sales_rep),
    locations: locations
      .filter((location) => location.accountId === account.id)
      .map(serializeLocation),
    contacts: contacts
      .filter((contact) => contact.accountId === account.id)
      .map(serializeContact),
  };
}

function serializeCatalogItem(product, pricing) {
  return {
    id: product.id,
    sku: product.sku,
    product_name: product.product_name,
    brand: product.brand,
    short_description: product.short_description,
    long_description: product.long_description,
    pack_size: product.pack_size,
    units_per_case: product.units_per_case,
    unit_weight_lbs: toNumber(product.unit_weight_lbs),
    case_weight_lbs: toNumber(product.case_weight_lbs),
    uom: product.uom,
    moq_cases: product.moq_cases,
    temperature_zone: product.temperature_zone,
    product_status: product.product_status,
    allergens: product.allergens,
    certifications: product.certifications,
    is_sample_eligible: Boolean(product.is_sample_eligible),
    category: product.category
      ? {
          id: product.category.id,
          category_name: product.category.category_name,
        }
      : null,
    contract_case_price: toNumber(pricing.case_price),
    contract_unit_price: toNumber(pricing.unit_price),
    contract_min_case_qty: pricing.min_case_qty || product.moq_cases || 1,
    contract_price_list: pricing.price_list_name,
    price_list_id: pricing.priceListId,
  };
}

function serializeOrder(order) {
  const items = (order.order_items_order || []).map((item) => ({
    id: item.id,
    quantity_cases: item.quantity_cases,
    case_price: toNumber(item.case_price),
    line_total: toNumber(item.line_total),
    fulfillment_status: item.fulfillment_status,
    line_notes: item.line_notes,
    product: serializeProductSummary(item.product),
  }));

  return {
    id: order.id,
    order_number: order.order_number,
    po_number: order.po_number,
    ordered_at: order.ordered_at,
    requested_delivery_date: order.requested_delivery_date,
    promised_delivery_date: order.promised_delivery_date,
    order_status: order.order_status,
    payment_terms: order.payment_terms,
    subtotal: toNumber(order.subtotal),
    tax_total: toNumber(order.tax_total),
    shipping_total: toNumber(order.shipping_total),
    order_total: toNumber(order.order_total),
    buyer_notes: order.buyer_notes,
    location: serializeLocation(order.location),
    buyer: serializeUser(order.buyer),
    items,
  };
}

function serializeQuote(quote) {
  const items = (quote.quote_items_quote || []).map((item) => ({
    id: item.id,
    quantity_cases: item.quantity_cases,
    case_price: toNumber(item.case_price),
    line_total: toNumber(item.line_total),
    line_notes: item.line_notes,
    product: serializeProductSummary(item.product),
  }));

  return {
    id: quote.id,
    quote_number: quote.quote_number,
    requested_at: quote.requested_at,
    expires_at: quote.expires_at,
    quote_status: quote.quote_status,
    quote_total: toNumber(quote.quote_total),
    notes: quote.notes,
    location: serializeLocation(quote.location),
    requested_by: serializeUser(quote.requested_by),
    owner: serializeUser(quote.owner),
    items,
  };
}

function serializeSampleRequest(sampleRequest) {
  return {
    id: sampleRequest.id,
    sample_request_number: sampleRequest.sample_request_number,
    sample_quantity: sampleRequest.sample_quantity,
    requested_at: sampleRequest.requested_at,
    needed_by: sampleRequest.needed_by,
    sample_status: sampleRequest.sample_status,
    notes: sampleRequest.notes,
    product: serializeProductSummary(sampleRequest.product),
    location: serializeLocation(sampleRequest.location),
    requested_by: serializeUser(sampleRequest.requested_by),
  };
}

function serializeSavedList(savedList) {
  const items = (savedList.saved_list_items_saved_list || []).map((item) => ({
    id: item.id,
    default_quantity_cases: item.default_quantity_cases,
    notes: item.notes,
    product: serializeProductSummary(item.product),
  }));

  return {
    id: savedList.id,
    list_name: savedList.list_name,
    list_type: savedList.list_type,
    notes: savedList.notes,
    createdAt: savedList.createdAt,
    updatedAt: savedList.updatedAt,
    owner: serializeUser(savedList.owner),
    items,
  };
}

async function ensureBuyerPortalSeeded() {
  if (!buyerPortalSeedPromise) {
    buyerPortalSeedPromise = Promise.resolve()
      .then(() => buyerPortalDemoSeeder.up())
      .catch((error) => {
        buyerPortalSeedPromise = null;
        throw error;
      });
  }

  await buyerPortalSeedPromise;
}

async function getAccounts(currentUser) {
  const buyerAccountIds = await getBuyerAccountIds(currentUser);
  const where = {
    is_active: true,
    default_price_listId: {
      [Op.ne]: null,
    },
  };

  if (buyerAccountIds !== null) {
    where.id = {
      [Op.in]: buyerAccountIds,
    };
  }

  const accounts = await db.accounts.findAll({
    where,
    include: [
      {
        model: db.price_lists,
        as: 'default_price_list',
        required: false,
      },
      {
        model: db.users,
        as: 'assigned_sales_rep',
        required: false,
      },
    ],
    order: [['createdAt', 'ASC']],
  });

  return accounts
    .map((account) => account.get({ plain: true }))
    .filter((account) =>
      account.default_price_list?.price_list_name?.startsWith(
        'Northstar Contract -',
      ),
    )
    .sort((left, right) => {
      if (left.account_name === 'Harbor Table Hospitality Group') {
        return -1;
      }

      if (right.account_name === 'Harbor Table Hospitality Group') {
        return 1;
      }

      return (left.account_name || '').localeCompare(right.account_name || '');
    });
}

async function getLocations(accountIds) {
  if (!accountIds.length) {
    return [];
  }

  const locations = await db.locations.findAll({
    where: {
      accountId: {
        [Op.in]: accountIds,
      },
      is_active: true,
    },
    include: [
      {
        model: db.contacts,
        as: 'default_contact',
        required: false,
      },
    ],
    order: [['createdAt', 'ASC']],
  });

  return locations.map((location) => location.get({ plain: true }));
}

async function getContacts(accountIds) {
  if (!accountIds.length) {
    return [];
  }

  const contacts = await db.contacts.findAll({
    where: {
      accountId: {
        [Op.in]: accountIds,
      },
      is_active: true,
    },
    order: [
      ['is_primary', 'DESC'],
      ['createdAt', 'ASC'],
    ],
  });

  return contacts.map((contact) => contact.get({ plain: true }));
}

async function getAccountPriceLists(account) {
  const accountPriceLists = await db.account_price_lists.findAll({
    where: {
      accountId: account.id,
    },
    include: [
      {
        model: db.price_lists,
        as: 'price_list',
        required: false,
      },
    ],
    order: [
      ['is_primary', 'DESC'],
      ['createdAt', 'ASC'],
    ],
  });

  const orderedPriceLists = [];
  const seen = new Set();

  accountPriceLists
    .map((item) => item.get({ plain: true }))
    .filter(
      (item) =>
        item.price_listId &&
        isCurrent(item.effective_start, item.effective_end),
    )
    .forEach((item) => {
      if (seen.has(item.price_listId)) {
        return;
      }

      seen.add(item.price_listId);
      orderedPriceLists.push({
        id: item.price_listId,
        price_list_name: item.price_list?.price_list_name || 'Contract pricing',
      });
    });

  if (account.default_price_listId && !seen.has(account.default_price_listId)) {
    orderedPriceLists.push({
      id: account.default_price_listId,
      price_list_name:
        account.default_price_list?.price_list_name || 'Default pricing',
    });
  }

  return orderedPriceLists;
}

async function getCatalogForAccount(account) {
  if (!account) {
    return [];
  }

  const priceLists = await getAccountPriceLists(account);
  const priceListIds = priceLists.map((item) => item.id);

  if (!priceListIds.length) {
    return [];
  }

  const priceListPriority = new Map(
    priceListIds.map((priceListId, index) => [priceListId, index]),
  );
  const priceListNames = new Map(
    priceLists.map((priceList) => [priceList.id, priceList.price_list_name]),
  );

  const priceListItems = await db.price_list_items.findAll({
    where: {
      price_listId: {
        [Op.in]: priceListIds,
      },
    },
    order: [['createdAt', 'DESC']],
  });

  const pricingByProduct = new Map();

  priceListItems
    .map((item) => item.get({ plain: true }))
    .filter((item) => isCurrent(item.effective_start, item.effective_end))
    .sort((left, right) => {
      const leftPriority = priceListPriority.get(left.price_listId) ?? 999;
      const rightPriority = priceListPriority.get(right.price_listId) ?? 999;
      return leftPriority - rightPriority;
    })
    .forEach((item) => {
      if (pricingByProduct.has(item.productId)) {
        return;
      }

      pricingByProduct.set(item.productId, {
        priceListId: item.price_listId,
        price_list_name:
          priceListNames.get(item.price_listId) || 'Contract pricing',
        case_price: item.case_price,
        unit_price: item.unit_price,
        min_case_qty: item.min_case_qty,
      });
    });

  const productIds = Array.from(pricingByProduct.keys());

  if (!productIds.length) {
    return [];
  }

  const products = await db.products.findAll({
    where: {
      id: {
        [Op.in]: productIds,
      },
      product_status: {
        [Op.in]: ACTIVE_PRODUCT_STATUSES,
      },
    },
    include: [
      {
        model: db.product_categories,
        as: 'category',
        required: false,
      },
    ],
    order: [
      [
        { model: db.product_categories, as: 'category' },
        'category_name',
        'ASC',
      ],
      ['product_name', 'ASC'],
    ],
  });

  return products.map((product) =>
    serializeCatalogItem(
      product.get({ plain: true }),
      pricingByProduct.get(product.id),
    ),
  );
}

async function getRecentOrders(accountId, currentUser) {
  const where = {
    accountId,
  };

  if (isCustomerBuyer(currentUser)) {
    where.buyerId = currentUser.id;
  }

  const orders = await db.orders.findAll({
    where,
    include: [
      {
        model: db.locations,
        as: 'location',
        required: false,
        include: [
          {
            model: db.contacts,
            as: 'default_contact',
            required: false,
          },
        ],
      },
      {
        model: db.users,
        as: 'buyer',
        required: false,
      },
      {
        model: db.order_items,
        as: 'order_items_order',
        required: false,
        include: [
          {
            model: db.products,
            as: 'product',
            required: false,
          },
        ],
      },
    ],
    order: [
      ['ordered_at', 'DESC'],
      ['createdAt', 'DESC'],
    ],
    limit: 6,
  });

  return orders.map((order) => serializeOrder(order.get({ plain: true })));
}

async function getRecentQuotes(accountId, currentUser) {
  const where = {
    accountId,
  };

  if (isCustomerBuyer(currentUser)) {
    where.requested_byId = currentUser.id;
  }

  const quotes = await db.quotes.findAll({
    where,
    include: [
      {
        model: db.locations,
        as: 'location',
        required: false,
        include: [
          {
            model: db.contacts,
            as: 'default_contact',
            required: false,
          },
        ],
      },
      {
        model: db.users,
        as: 'requested_by',
        required: false,
      },
      {
        model: db.users,
        as: 'owner',
        required: false,
      },
      {
        model: db.quote_items,
        as: 'quote_items_quote',
        required: false,
        include: [
          {
            model: db.products,
            as: 'product',
            required: false,
          },
        ],
      },
    ],
    order: [
      ['requested_at', 'DESC'],
      ['createdAt', 'DESC'],
    ],
    limit: 4,
  });

  return quotes.map((quote) => serializeQuote(quote.get({ plain: true })));
}

async function getRecentSamples(accountId, currentUser) {
  const where = {
    accountId,
  };

  if (isCustomerBuyer(currentUser)) {
    where.requested_byId = currentUser.id;
  }

  const sampleRequests = await db.sample_requests.findAll({
    where,
    include: [
      {
        model: db.products,
        as: 'product',
        required: false,
      },
      {
        model: db.locations,
        as: 'location',
        required: false,
        include: [
          {
            model: db.contacts,
            as: 'default_contact',
            required: false,
          },
        ],
      },
      {
        model: db.users,
        as: 'requested_by',
        required: false,
      },
    ],
    order: [
      ['requested_at', 'DESC'],
      ['createdAt', 'DESC'],
    ],
    limit: 4,
  });

  return sampleRequests.map((sampleRequest) =>
    serializeSampleRequest(sampleRequest.get({ plain: true })),
  );
}

async function getSavedLists(accountId, currentUser) {
  const where = {
    accountId,
  };

  if (isCustomerBuyer(currentUser)) {
    where.ownerId = currentUser.id;
  }

  const savedLists = await db.saved_lists.findAll({
    where,
    include: [
      {
        model: db.users,
        as: 'owner',
        required: false,
      },
      {
        model: db.saved_list_items,
        as: 'saved_list_items_saved_list',
        required: false,
        include: [
          {
            model: db.products,
            as: 'product',
            required: false,
          },
        ],
      },
    ],
    order: [
      ['updatedAt', 'DESC'],
      ['createdAt', 'DESC'],
    ],
    limit: 6,
  });

  return savedLists.map((savedList) =>
    serializeSavedList(savedList.get({ plain: true })),
  );
}

module.exports = class BuyerPortalService {
  static async workspace(filter = {}, currentUser) {
    await ensureBuyerPortalSeeded();

    const accounts = await getAccounts(currentUser);
    const accountIds = accounts.map((account) => account.id);
    const locations = await getLocations(accountIds);
    const contacts = await getContacts(accountIds);
    const serializedAccounts = accounts.map((account) =>
      serializeAccount(account, locations, contacts),
    );

    const selectedAccount =
      serializedAccounts.find((account) => account.id === filter.accountId) ||
      serializedAccounts[0] ||
      null;

    const selectedLocation =
      selectedAccount?.locations.find(
        (location) => location.id === filter.locationId,
      ) ||
      selectedAccount?.locations[0] ||
      null;

    const selectedAccountWithPricing = accounts.find(
      (account) => account.id === selectedAccount?.id,
    );

    const catalog = await getCatalogForAccount(selectedAccountWithPricing);
    const recentOrders = selectedAccount
      ? await getRecentOrders(selectedAccount.id, currentUser)
      : [];
    const recentQuotes = selectedAccount
      ? await getRecentQuotes(selectedAccount.id, currentUser)
      : [];
    const recentSamples = selectedAccount
      ? await getRecentSamples(selectedAccount.id, currentUser)
      : [];
    const savedLists = selectedAccount
      ? await getSavedLists(selectedAccount.id, currentUser)
      : [];

    return {
      accounts: serializedAccounts,
      selectedAccountId: selectedAccount?.id || '',
      selectedLocationId: selectedLocation?.id || '',
      catalog,
      recentOrders,
      recentQuotes,
      recentSamples,
      savedLists,
      roleContext: {
        isBuyerAdmin: isCustomerBuyerAdmin(currentUser),
        isBuyerUser: isCustomerBuyer(currentUser),
      },
    };
  }

  static async createSavedList(payload = {}, currentUser) {
    await ensureBuyerPortalSeeded();

    if (!currentUser?.id) {
      throw makeError('You must be signed in to save a reorder list.');
    }

    if (!payload.accountId) {
      throw makeError('Choose an account before saving a reorder list.');
    }

    await assertAccountAccess(payload.accountId, currentUser);

    if (!payload.listName || !String(payload.listName).trim()) {
      throw makeError('Give this reorder list a name.');
    }

    if (!Array.isArray(payload.items) || !payload.items.length) {
      throw makeError(
        'Add at least one product before saving the reorder list.',
      );
    }

    const account = await db.accounts.findOne({
      where: {
        id: payload.accountId,
        is_active: true,
      },
      include: [
        {
          model: db.price_lists,
          as: 'default_price_list',
          required: false,
        },
      ],
    });

    if (!account) {
      throw makeError('The selected account could not be found.');
    }

    const catalog = await getCatalogForAccount(account.get({ plain: true }));
    const catalogByProductId = new Map(catalog.map((item) => [item.id, item]));

    const listItems = payload.items.map((item) => {
      const product = catalogByProductId.get(item.productId);
      const quantityCases = Number.parseInt(String(item.quantityCases), 10);

      if (!product) {
        throw makeError(
          'One of the selected products is no longer on the contract catalog.',
        );
      }

      if (!Number.isInteger(quantityCases) || quantityCases <= 0) {
        throw makeError(
          'Enter a valid case quantity for ' + product.product_name + '.',
        );
      }

      const minQty = product.contract_min_case_qty || product.moq_cases || 1;
      if (quantityCases < minQty) {
        throw makeError(
          product.product_name +
            ' requires a minimum of ' +
            minQty +
            ' case(s).',
        );
      }

      return {
        product,
        quantityCases,
      };
    });

    const transaction = await db.sequelize.transaction();

    try {
      const savedList = await db.saved_lists.create(
        {
          list_name: String(payload.listName).trim(),
          list_type: 'reorder',
          notes: payload.listNotes || null,
          createdById: currentUser.id,
          updatedById: currentUser.id,
        },
        { transaction },
      );

      await savedList.setAccount(account.id, { transaction });
      await savedList.setOwner(currentUser.id, { transaction });

      for (const item of listItems) {
        const savedListItem = await db.saved_list_items.create(
          {
            default_quantity_cases: item.quantityCases,
            notes: item.product.pack_size || null,
            createdById: currentUser.id,
            updatedById: currentUser.id,
          },
          { transaction },
        );

        await savedListItem.setSaved_list(savedList.id, { transaction });
        await savedListItem.setProduct(item.product.id, { transaction });
      }

      await transaction.commit();

      const createdSavedList = await db.saved_lists.findByPk(savedList.id, {
        include: [
          {
            model: db.users,
            as: 'owner',
            required: false,
          },
          {
            model: db.saved_list_items,
            as: 'saved_list_items_saved_list',
            required: false,
            include: [
              {
                model: db.products,
                as: 'product',
                required: false,
              },
            ],
          },
        ],
      });

      return serializeSavedList(createdSavedList.get({ plain: true }));
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async createOrder(payload = {}, currentUser) {
    await ensureBuyerPortalSeeded();

    if (!currentUser?.id) {
      throw makeError('You must be signed in to place a purchase order.');
    }

    if (!payload.accountId) {
      throw makeError('Choose an account before placing an order.');
    }

    await assertAccountAccess(payload.accountId, currentUser);

    if (!payload.locationId) {
      throw makeError('Choose a delivery location before placing an order.');
    }

    if (!payload.poNumber || !String(payload.poNumber).trim()) {
      throw makeError('PO number is required for this order.');
    }

    if (!Array.isArray(payload.items) || !payload.items.length) {
      throw makeError('Add at least one product to the draft order.');
    }

    const account = await db.accounts.findOne({
      where: {
        id: payload.accountId,
        is_active: true,
      },
      include: [
        {
          model: db.price_lists,
          as: 'default_price_list',
          required: false,
        },
      ],
    });

    if (!account) {
      throw makeError('The selected account could not be found.');
    }

    const location = await db.locations.findOne({
      where: {
        id: payload.locationId,
        accountId: payload.accountId,
        is_active: true,
      },
    });

    if (!location) {
      throw makeError(
        'The selected delivery location is invalid for this account.',
      );
    }

    const catalog = await getCatalogForAccount(account.get({ plain: true }));
    const catalogByProductId = new Map(catalog.map((item) => [item.id, item]));

    const orderItems = payload.items.map((item) => {
      const product = catalogByProductId.get(item.productId);
      const quantityCases = Number.parseInt(String(item.quantityCases), 10);

      if (!product) {
        throw makeError(
          'One of the selected products is no longer on the contract catalog.',
        );
      }

      if (!Number.isInteger(quantityCases) || quantityCases <= 0) {
        throw makeError(
          `Enter a valid case quantity for ${product.product_name}.`,
        );
      }

      const minQty = product.contract_min_case_qty || product.moq_cases || 1;
      if (quantityCases < minQty) {
        throw makeError(
          `${product.product_name} requires a minimum of ${minQty} case(s).`,
        );
      }

      if (!product.contract_case_price) {
        throw makeError(
          `${product.product_name} does not have active contract pricing.`,
        );
      }

      return {
        product,
        quantityCases,
        casePrice: product.contract_case_price,
        lineTotal: Number(
          (product.contract_case_price * quantityCases).toFixed(2),
        ),
      };
    });

    const subtotal = Number(
      orderItems.reduce((sum, item) => sum + item.lineTotal, 0).toFixed(2),
    );

    const transaction = await db.sequelize.transaction();

    try {
      const order = await db.orders.create(
        {
          order_number: buildOrderNumber(),
          po_number: String(payload.poNumber).trim(),
          ordered_at: new Date(),
          requested_delivery_date: payload.requestedDeliveryDate || null,
          promised_delivery_date: null,
          order_status: 'submitted',
          payment_terms: payload.paymentTerms || 'net_30',
          subtotal,
          tax_total: 0,
          shipping_total: 0,
          order_total: subtotal,
          buyer_notes: payload.buyerNotes || null,
          internal_notes: null,
          createdById: currentUser.id,
          updatedById: currentUser.id,
        },
        { transaction },
      );

      await order.setAccount(account.id, { transaction });
      await order.setLocation(location.id, { transaction });
      await order.setBuyer(currentUser.id, { transaction });

      for (const item of orderItems) {
        const orderItem = await db.order_items.create(
          {
            quantity_cases: item.quantityCases,
            case_price: item.casePrice,
            line_total: item.lineTotal,
            fulfillment_status: 'pending',
            line_notes: item.product.pack_size,
            createdById: currentUser.id,
            updatedById: currentUser.id,
          },
          { transaction },
        );

        await orderItem.setOrder(order.id, { transaction });
        await orderItem.setProduct(item.product.id, { transaction });
      }

      await transaction.commit();

      const createdOrder = await db.orders.findByPk(order.id, {
        include: [
          {
            model: db.locations,
            as: 'location',
            required: false,
            include: [
              {
                model: db.contacts,
                as: 'default_contact',
                required: false,
              },
            ],
          },
          {
            model: db.users,
            as: 'buyer',
            required: false,
          },
          {
            model: db.order_items,
            as: 'order_items_order',
            required: false,
            include: [
              {
                model: db.products,
                as: 'product',
                required: false,
              },
            ],
          },
        ],
      });

      return serializeOrder(createdOrder.get({ plain: true }));
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async createSampleRequest(payload = {}, currentUser) {
    await ensureBuyerPortalSeeded();

    if (!currentUser?.id) {
      throw makeError('You must be signed in to request a sample.');
    }

    if (!payload.accountId) {
      throw makeError('Choose an account before requesting a sample.');
    }

    await assertAccountAccess(payload.accountId, currentUser);

    if (!payload.locationId) {
      throw makeError('Choose a delivery location before requesting a sample.');
    }

    if (!payload.productId) {
      throw makeError('Choose a sample-eligible product.');
    }

    const quantity = Number.parseInt(String(payload.sampleQuantity), 10);
    if (!Number.isInteger(quantity) || quantity <= 0) {
      throw makeError('Enter a valid sample quantity.');
    }

    const account = await db.accounts.findOne({
      where: {
        id: payload.accountId,
        is_active: true,
      },
      include: [
        {
          model: db.price_lists,
          as: 'default_price_list',
          required: false,
        },
      ],
    });

    if (!account) {
      throw makeError('The selected account could not be found.');
    }

    const location = await db.locations.findOne({
      where: {
        id: payload.locationId,
        accountId: payload.accountId,
        is_active: true,
      },
    });

    if (!location) {
      throw makeError(
        'The selected delivery location is invalid for this account.',
      );
    }

    const catalog = await getCatalogForAccount(account.get({ plain: true }));
    const product = catalog.find((item) => item.id === payload.productId);

    if (!product) {
      throw makeError(
        'The selected product is no longer on the contract catalog.',
      );
    }

    if (!product.is_sample_eligible) {
      throw makeError(`${product.product_name} is not sample eligible.`);
    }

    const sampleRequest = await db.sample_requests.create({
      sample_request_number: buildSampleRequestNumber(),
      sample_quantity: quantity,
      requested_at: new Date(),
      needed_by: payload.neededBy || null,
      sample_status: 'requested',
      notes: payload.notes || null,
      accountId: account.id,
      locationId: location.id,
      requested_byId: currentUser.id,
      productId: product.id,
      createdById: currentUser.id,
      updatedById: currentUser.id,
    });

    const createdSampleRequest = await db.sample_requests.findByPk(
      sampleRequest.id,
      {
        include: [
          {
            model: db.products,
            as: 'product',
            required: false,
          },
          {
            model: db.locations,
            as: 'location',
            required: false,
            include: [
              {
                model: db.contacts,
                as: 'default_contact',
                required: false,
              },
            ],
          },
          {
            model: db.users,
            as: 'requested_by',
            required: false,
          },
        ],
      },
    );

    return serializeSampleRequest(createdSampleRequest.get({ plain: true }));
  }
};
