import type {Locale} from './catalog';

const ignored=/[,٬\s\u200e\u200f\u061c]/g;
const digitOrDecimal=/[0-9۰-۹٠-٩.٫]/;
const ascii=(value:string)=>value.replace(/[۰-۹]/g,d=>String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d))).replace(/[٠-٩]/g,d=>String('٠١٢٣٤٥٦٧٨٩'.indexOf(d))).replace(ignored,'').replace(/٫/g,'.');

/** Normalize an editable, non-negative decimal without converting it to a Number. */
export function normalizeLocalizedNumber(value:string,maxLength=Infinity):string|null{
 const raw=ascii(value);
 if(raw==='')return '';
 if(!/^\d*(?:\.\d*)?$/.test(raw))return null;
 const [whole,fraction]=raw.split('.');
 const canonical=(whole.replace(/^0+(?=\d)/,'')||'0')+(fraction===undefined?'':'.'+fraction);
 return canonical.length>maxLength?null:canonical;
}

/** An empty model has a visible zero, while remaining empty for financial validation. */
export function formatLocalizedNumberInput(value:string,locale:Locale='fa'):string{
 const canonical=normalizeLocalizedNumber(value);
 const [whole,fraction]=(canonical||'0').split('.');
 const english=locale==='en';
 const formatted=whole.replace(/\B(?=(\d{3})+(?!\d))/g,english?',':'٬')+(fraction===undefined?'':(english?'.':'٫')+fraction);
 return english?formatted:formatted.replace(/\d/g,d=>(locale==='ar'?'٠١٢٣٤٥٦٧٨٩':'۰۱۲۳۴۵۶۷۸۹')[Number(d)]);
}

/** Map a count of canonical characters to the same position in a grouped input. */
export function localizedNumberCaret(formatted:string,canonicalOffset:number):number{
 if(canonicalOffset<=0)return 0;
 let count=0;
 for(let index=0;index<formatted.length;index++){
  if(digitOrDecimal.test(formatted[index])&&++count===canonicalOffset)return index+1;
 }
 return formatted.length;
}

export interface LocalizedNumberEdit {value:string;formatted:string;caret:number}

/** Interpret a browser edit, keeping the caret beside the same decimal digit. */
export function editLocalizedNumber(raw:string,caret:number,locale:Locale='fa',maxLength=80):LocalizedNumberEdit|null{
 const value=normalizeLocalizedNumber(raw,maxLength);
 if(value===null)return null;
 const formatted=formatLocalizedNumberInput(value,locale);
 if(value==='')return {value,formatted,caret:formatted.length};
 const ungrouped=ascii(raw),prefix=ascii(raw.slice(0,Math.max(0,caret)));
 const whole=ungrouped.split('.')[0],canonicalWhole=value.split('.')[0];
 const removed=whole.length-canonicalWhole.length;
 const offset=Math.max(0,prefix.length-removed);
 return {value,formatted,caret:localizedNumberCaret(formatted,offset)};
}

/** Treat grouping separators as decoration when deleting with the keyboard. */
export function deleteLocalizedNumber(value:string,locale:Locale,selectionStart:number,selectionEnd:number,direction:'backward'|'forward',maxLength=80):LocalizedNumberEdit|null{
 const formatted=formatLocalizedNumberInput(value,locale);
 let start=Math.max(0,Math.min(selectionStart,formatted.length)),end=Math.max(start,Math.min(selectionEnd,formatted.length));
 if(start===end){
  if(direction==='backward'){
   while(start>0&&!digitOrDecimal.test(formatted[start-1]))start--;
   if(start>0)start--;
  }else{
   while(end<formatted.length&&!digitOrDecimal.test(formatted[end]))end++;
   if(end<formatted.length)end++;
  }
 }
 return editLocalizedNumber(formatted.slice(0,start)+formatted.slice(end),start,locale,maxLength);
}
