'use strict';

const config = require('./config');

function normalizeUsername(value) {
  return String(value || '').trim();
}

function isValidUsername(value) {
  const username = normalizeUsername(value);
  return username.length >= 3 && username.length <= 80 && /^[\p{L}\p{N}_.@-]+$/u.test(username);
}

function isStrongEnoughPassword(value) {
  return typeof value === 'string' && value.length >= 8 && value.length <= 200;
}

function nullableText(value, maxLength = 255) {
  const text = String(value ?? '').trim();
  if (!text) return null;
  return text.slice(0, maxLength);
}

function nullableNumber(value, { min = 0, max = 999999999 } = {}) {
  if (value === '' || value === null || value === undefined) return null;
  const number = Number(value);
  if (!Number.isFinite(number) || number < min || number > max) return null;
  return number;
}

function round(value, decimals) {
  if (value === null || value === undefined || !Number.isFinite(value)) return null;
  const factor = 10 ** decimals;
  return Math.round((value + Number.EPSILON) * factor) / factor;
}

function calculateLine(raw) {
  const quantity = nullableNumber(raw.quantity, { min: 0, max: 9999999 });
  const packSize = nullableNumber(raw.packSize, { min: 0, max: 9999999 });
  const unitCost = nullableNumber(raw.unitCost, { min: 0, max: 999999999 });
  const salePrice = nullableNumber(raw.salePrice, { min: 0, max: 999999999 });

  const totalUnits = quantity !== null && packSize !== null ? round(quantity * packSize, 3) : null;
  const totalCost = quantity !== null && unitCost !== null ? round(quantity * unitCost, 2) : null;
  const costPerUnit = totalUnits && totalCost !== null ? round(totalCost / totalUnits, 4) : null;

  return {
    quantity,
    packSize,
    totalUnits,
    unitCost,
    totalCost,
    costPerUnit,
    salePrice,
  };
}

function riyadhDate(date = new Date()) {
  const parts = new Intl.DateTimeFormat('en-CA', {
    timeZone: config.timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).formatToParts(date);
  const map = Object.fromEntries(parts.map((part) => [part.type, part.value]));
  return `${map.year}-${map.month}-${map.day}`;
}

function isIsoDate(value) {
  if (!/^\d{4}-\d{2}-\d{2}$/.test(String(value || ''))) return false;
  const [year, month, day] = value.split('-').map(Number);
  const date = new Date(Date.UTC(year, month - 1, day));
  return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
}

function arabicDate(value) {
  if (!value) return '';
  const date = typeof value === 'string' ? new Date(`${value.slice(0, 10)}T12:00:00Z`) : new Date(value);
  return new Intl.DateTimeFormat('ar-SA', {
    timeZone: config.timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).format(date);
}

function requestIp(req) {
  return String(req.ip || req.socket?.remoteAddress || '').slice(0, 64) || null;
}

function asyncHandler(fn) {
  return function wrapped(req, res, next) {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

module.exports = {
  normalizeUsername,
  isValidUsername,
  isStrongEnoughPassword,
  nullableText,
  nullableNumber,
  round,
  calculateLine,
  riyadhDate,
  isIsoDate,
  arabicDate,
  requestIp,
  asyncHandler,
};
