import moment from 'moment';
import 'moment/locale/id';
import { twMerge } from 'tailwind-merge';
import clsx, { ClassValue } from 'clsx';
import { SidebarMenuItem } from '@/components/molecules/SidebarMenu';
import { OptionType } from '@/components/input/SelectInput';
import {
  ConstantsApiResponse,
  ProductFlagMapping,
  TransformedConstants,
} from '@/types/api/constants/constants';

// set locale globally
moment.locale('id');

export const sleep = (ms: number = 1000) =>
  new Promise((resolve) => setTimeout(resolve, ms));

export const formatDate = (date: moment.MomentInput, format?: string) => {
  if (!date) return '-';

  return moment(date).format(format);
};

export const cn = (...inputs: ClassValue[]) => {
  return twMerge(clsx(inputs));
};

export const formatNumber = (
  value: number | bigint | Intl.StringNumericLiteral,
  locale = 'id-ID',
  minimumFractionDigits = 0,
  maximumFractionDigits = 2
) => {
  return new Intl.NumberFormat(locale, {
    minimumFractionDigits,
    maximumFractionDigits,
  }).format(value);
};

export const safeRound = (num: number, decimals: number) => {
  const factor = 10 ** decimals;
  return Math.round((num + Number.EPSILON) * factor) / factor;
};

export const formatTitleCase = (value: string) => {
  return value
    .toLowerCase()
    .replace(/_/g, ' ')
    .split(' ')
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
    .join(' ');
};

export function formatVechicleNumber(value: string): string {
  let result = '';
  for (let i = 0; i < (value?.length ?? 0); i++) {
    const curr = value[i];
    const prev = value[i - 1];

    // Cek apakah terjadi perpindahan dari huruf ke angka atau angka ke huruf
    if (i > 0) {
      const isCurrDigit = /\d/.test(curr);
      const isPrevDigit = /\d/.test(prev);

      if (isCurrDigit !== isPrevDigit) {
        result += ' ';
      }
    }

    result += curr;
  }

  return result.trim().replace(/\s+/g, ' ');
}

export const formatCurrency = (
  value: number | bigint | Intl.StringNumericLiteral,
  currency = 'IDR',
  locale = 'id-ID',
  minimumFractionDigits = 0,
  maximumFractionDigits = 2
) => {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
    minimumFractionDigits,
    maximumFractionDigits,
  }).format(value);
};

/**
 * Retrieves a nested value from an object using a dot-delimited key path.
 * Supports array indexes (e.g., "users.0.name") and returns a default value
 * if the path does not exist.
 *
 * @param obj - The source object to search.
 * @param path - Dot-delimited key string (e.g., "user.address.city").
 * @param defaultValue - Optional value to return if the key path is not found.
 * @returns The value found at the specified path, or the default value.
 */
export function getByPath<T, D = undefined>(
  obj: T,
  path: string,
  defaultValue?: D
): D {
  if (obj == null) return defaultValue as D;
  if (!path) return obj as D;

  const segments = path.split('.').filter(Boolean);
  let cur: { [key: string]: unknown } = obj;

  for (const seg of segments) {
    if (cur == null) return defaultValue as D;
    const key: string | number =
      Array.isArray(cur) && /^\d+$/.test(seg) ? Number(seg) : seg;
    if (Object(cur) !== cur || !(key in cur)) {
      return defaultValue as D;
    }
    cur = cur[key] as { [key: string]: unknown };
  }

  return cur as D;
}

export const convertRowSelectionArrToObj = (
  rowSelectionArr: string[] | number[]
) => {
  const result: Record<string | number, boolean> = {};

  rowSelectionArr.forEach((item) => {
    result[item] = true;
  });

  return result;
};

export const convertRowSelectionObjToArr = (
  rowSelection: string[] | number[]
) => {
  const result = Object.keys(rowSelection).map(Number);

  return result;
};

export const isPathActive = (pathname: string, link?: string) => {
  if (!link) return false;

  const splittedPathname = pathname.split('/');
  const splittedLink = link.split('/');

  const isActiveLinkValid = splittedLink.every((linkChunk, idx) => {
    return linkChunk === splittedPathname[idx];
  });

  return pathname.startsWith(link) && isActiveLinkValid;
};

export function findMenuPath(
  menus: readonly SidebarMenuItem[],
  pathname: string,
  parents: SidebarMenuItem[] = []
): SidebarMenuItem[] | null {
  for (const menu of menus) {
    const currentPath = [...parents, menu];

    // Exact match
    if (menu.link === pathname) {
      return currentPath;
    }

    // Prefix match (useful for pages like /add, /edit, etc.)
    if (pathname.startsWith(menu.link + '/')) {
      if (!menu.submenu) {
        return currentPath;
      }
    }

    // Search children
    if (menu.submenu) {
      const found = findMenuPath(menu.submenu, pathname, currentPath);
      if (found) return found;
    }
  }

  return null;
}

/**
 * Transform a string value to OptionType with formatted label
 * Example: "AYAM-AFKIR" -> { label: "Ayam Afkir", value: "AYAM-AFKIR" }
 */
export function toOption(value: string): OptionType {
  return {
    value,
    label: formatConstantLabel(value),
  };
}

/**
 * Format constant label by:
 * 1. Replacing underscores/hyphens with spaces
 * 2. Converting to title case
 * 3. Handling special cases
 */
export function formatConstantLabel(value: string): string {
  const specialCases: Record<string, string> = {
    'PRE-STARTER': 'Pre Starter',
    BOP: 'BOP',
    SAPRONAK: 'SAPRONAK',
    OVK: 'OVK',
    DOC: 'DOC',
  };

  if (specialCases[value]) {
    return specialCases[value];
  }

  const withSpaces = value.replace(/[-_]/g, ' ');

  return formatTitleCase(withSpaces);
}

/**
 * Transform product_flag_mapping from API format to UI format
 */
export function transformProductFlagMapping(
  mapping: ConstantsApiResponse['product_flag_mapping']
): ProductFlagMapping {
  return {
    flags: mapping.flags.map(toOption),
    options: mapping.options.map((opt) => ({
      flag: toOption(opt.flag),
      sub_flags: opt.sub_flags.map(toOption),
      allow_without_sub_flag: opt.allow_without_sub_flag,
    })),
    sub_flag_to_flag: mapping.sub_flag_to_flag,
  };
}

/**
 * Transform approval workflows from API format to UI format
 */
export function transformApprovalWorkflows(
  workflows: ConstantsApiResponse['approval_workflows']
) {
  return workflows.map((workflow) => ({
    key: workflow.key,
    steps: workflow.steps.map((step) => ({
      value: String(step.step_number),
      label: step.step_name,
    })),
  }));
}

/**
 * Transform adjustment transaction subtypes from API format to UI format
 */
export function transformAdjustmentSubtypes(
  subtypes: ConstantsApiResponse['adjustment']['transaction_subtypes']
) {
  return {
    RECORDING: subtypes.RECORDING.map(toOption),
    PENJUALAN: subtypes.PENJUALAN.map(toOption),
    PEMBELIAN: subtypes.PEMBELIAN.map(toOption),
  };
}

/**
 * Transform legacy flag aliases from API format to UI format
 */
export function transformLegacyFlagAliases(
  aliases: ConstantsApiResponse['legacy_flag_aliases']
): OptionType[] {
  return Object.entries(aliases).map(([key]) => ({
    value: key,
    label: formatConstantLabel(key),
  }));
}

/**
 * Transform the entire constants API response to UI format
 */
export function transformConstants(
  data: ConstantsApiResponse
): TransformedConstants {
  return {
    warehouse_types: data.warehouse_types.map(toOption),
    supplier_categories: data.supplier_categories.map(toOption),
    customer_supplier_types: data.customer_supplier_types.map(toOption),
    adjustment: {
      transaction_subtypes: transformAdjustmentSubtypes(
        data.adjustment.transaction_subtypes
      ),
    },
    approval_workflows: transformApprovalWorkflows(data.approval_workflows),
    flags: data.flags.map(toOption),
    product_flag_mapping: transformProductFlagMapping(
      data.product_flag_mapping
    ),
    legacy_flag_aliases: transformLegacyFlagAliases(data.legacy_flag_aliases),
    stock_log: {
      log_types: data.stock_log.log_types.map(toOption),
      transaction_types: data.stock_log.transaction_types.map(toOption),
    },
  };
}
