import{copyFile as e,mkdir as t,readdir as s,rmdir as i,stat as r,unlink as a,utimes as c}from"fs";import{promisify as h}from"util";const n=h(e),d=h(t),u=h(s),o=h(i),m=h(r),f=h(a),l=h(c);class w{constructor(e,t){this.cache={},this.exclude={},this.recursive=e,this.delete=t}async start(e,t,s){return e&&t?"string"!=typeof e||"string"!=typeof t?{res:!1,err:"Parameters 'src' and 'dest' must be string paths"}:(e=this.fix(e),t=this.fix(t),await this.prepare(e,"src")&&await this.prepare(t,"dest")?(s.forEach(t=>this.exclude[e+"/"+t]=!0),await this.walk(e,t),{res:'Directory "'+e+'" reflected to "'+t+'"',err:!1}):{res:!1,err:"Parameters 'src' and 'dest' must be a directory"}):{res:!1,err:"Parameters 'src' and 'dest' must be defined"}}async prepare(e,t){return!!await this.is_dir(e)||!await this.read(e)&&("src"!=t&&(this.cache[e]=!0,await d(e),!0))}async walk(e,t){const s=[],i=await u(e),r=await u(t);if(this.delete&&r.length){const e={};i.forEach(t=>e[t]=!0);r.filter(s=>!e[s]&&!this.exclude[t+"/"+s]).forEach(e=>s.push(this.remove(t+"/"+e)))}return i.forEach(i=>s.push(this.sync(e+"/"+i,t+"/"+i))),Promise.all(s)}async sync(e,t){return!!this.exclude[e]||(await this.is_dir(e)?!this.recursive||(await this.prepare(t,"dest"),this.walk(e,t)):!(!await this.read(t)||this.is_different(e,t))||(await n(e,t),l(t,this.cache[e].atime,this.cache[e].mtime)))}async read(e){if(void 0===this.cache[e])try{return this.cache[e]=await m(e)}catch(t){return this.cache[e]=!1,!1}return this.cache[e]}async remove(e){if(!await this.is_dir(e))return this.cache[e]=!1,f(e);const t=await u(e);return await Promise.all(t.map(t=>this.remove(e+"/"+t))),this.cache[e]=!1,o(e)}async is_dir(e){return!!await this.read(e)&&this.cache[e].isDirectory()}is_different(e,t){return this.cache[e].size!=this.cache[t].size||this.cache[e].mtime.getTime()!=this.cache[t].mtime.getTime()}fix(e){const t=e.slice(-1);return"/"==t||"\\"==t?e.slice(0,-1):e}}export default function({src:e,dest:t,recursive:s=!0,delete:i=!0,exclude:r=[]}){return new w(s,i).start(e,t,r)}
