import {canEditTokenization,getAssetType,validateAssetFields,type TokenizationRequest} from './tokenization.ts';

export type PendingTokenizationDocuments = Record<string,File|null>;

type DraftTransport = {
 start:(assetType:string)=>Promise<TokenizationRequest>;
 save:(request:TokenizationRequest,fields:Record<string,string>,wizardStep?:number)=>Promise<TokenizationRequest>;
 upload:(request:TokenizationRequest,slot:string,file:File)=>Promise<TokenizationRequest>;
 remove:(request:TokenizationRequest,documentId:string)=>Promise<TokenizationRequest>;
};
type PersistDraftOptions = {
 request:TokenizationRequest|null;
 assetType:string;
 fields:Record<string,string>;
 documents:PendingTokenizationDocuments;
 complete:boolean;
 wizardStep?:number;
 transport:DraftTransport;
 checkpoint:(request:TokenizationRequest,completedSlot?:string)=>void;
};

function assertEditable(request:TokenizationRequest,assetType:string){
 if(!canEditTokenization(request.status))throw new Error('این پرونده در حال حاضر قابل ویرایش نیست. آخرین وضعیت را دریافت کنید.');
 if(request.asset_type!==assetType)throw new Error('نوع دارایی پروندهٔ ذخیره‌شده قابل تغییر نیست. برای نوع دیگری از دارایی، پروندهٔ جدید بسازید.');
}
function sameFields(left:Record<string,string>,right:Record<string,string>){
 const keys=Object.keys(left);
 return keys.length===Object.keys(right).length&&keys.every(key=>left[key]===right[key]);
}

/** A late read must never downgrade a request already saved in this workspace. */
export function latestTokenizationSnapshot(incoming:TokenizationRequest,known:TokenizationRequest|null|undefined){
 return known?.id===incoming.id&&known.version>=incoming.version?known:incoming;
}

/** Requests are retained; an older GET may omit a draft created while it was in flight. */
export function mergeTokenizationSnapshots(known:readonly TokenizationRequest[],incoming:readonly TokenizationRequest[]){
 const requests=new Map(known.map(request=>[request.id,request]));
 for(const request of incoming)requests.set(request.id,latestTokenizationSnapshot(request,requests.get(request.id)));
 return [...requests.values()].sort((left,right)=>Date.parse(right.updated_at)-Date.parse(left.updated_at)||left.id.localeCompare(right.id));
}

/** A completed upload must not remove a newer file selected while it was in flight. */
export function clearCompletedDraftDocument(pending:PendingTokenizationDocuments,snapshot:PendingTokenizationDocuments,slot:string){
 if(!Object.hasOwn(pending,slot)||pending[slot]!==snapshot[slot])return pending;
 const next={...pending};delete next[slot];return next;
}

/** Shared serialized persistence for automatic draft saves and explicit submission. */
export async function persistTokenizationDraft({request,assetType,fields,documents,complete,wizardStep,transport,checkpoint}:PersistDraftOptions):Promise<TokenizationRequest>{
 if(wizardStep!==undefined&&(!Number.isInteger(wizardStep)||wizardStep<0||wizardStep>3))throw new Error('مرحلهٔ پیش‌نویس معتبر نیست.');
 const normalized=validateAssetFields(assetType,fields,{partial:!complete});
 const asset=getAssetType(assetType)!;
 if(request)assertEditable(request,assetType);
 // Snapshot the queue: the caller may remove a completed slot inside checkpoint.
 const pending=Object.entries(documents);
 for(const [slot,file] of pending){
  if(!asset.documents.some(document=>document.key===slot))throw new Error('این مدرک به نوع دارایی انتخاب‌شده مربوط نیست.');
  if(file===null)continue;
  if(!(file instanceof File)||file.size<=0||file.size>10*1024*1024)throw new Error('حجم هر مدرک باید بیشتر از صفر و حداکثر ۱۰ مگابایت باشد.');
  if(!['application/pdf','image/jpeg','image/png'].includes(file.type))throw new Error('مدرک را به‌صورت PDF، JPG یا PNG انتخاب کنید.');
 }

 let latest=request;
 if(!latest){
  latest=await transport.start(assetType);
  assertEditable(latest,assetType);
  // The server reuses same-type drafts. Never overwrite an existing application.
  if(Object.keys(latest.fields).length||latest.documents.length)throw new Error('یک پیش‌نویس برای این نوع دارایی از قبل دارید. آن را از فهرست پرونده‌ها باز کنید؛ اطلاعات فعلی شما هنوز موقت نگهداری می‌شود.');
  checkpoint(latest);
 }
 const savedFields=validateAssetFields(assetType,latest.fields,{partial:true});
 if(!sameFields(normalized,savedFields)||(wizardStep!==undefined&&wizardStep!==latest.wizard_step)){
  latest=await transport.save(latest,normalized,wizardStep);
  checkpoint(latest);
 }
 for(const [slot,file] of pending){
  assertEditable(latest,assetType);
  if(file){
   latest=await transport.upload(latest,slot,file);
  }else{
   const existing=latest.documents.find(document=>document.slot===slot);
   if(existing)latest=await transport.remove(latest,existing.id);
  }
  // Preserve completed work and its new version if a later operation fails.
  checkpoint(latest,slot);
 }
 return latest;
}
