// import-flow.js — v2.1.6
// premierepro API: docs say sync, runtime returns Promises. Await everything.

(function () {
  const Import = {};

  const fs = require('fs');
  // window.path is a UXP global (v6.4+) — no require('path')
  let os; try { os = require('os'); } catch (_) { os = {}; }
  let uxpFs; try { uxpFs = require('uxp').storage.localFileSystem; } catch (_) { uxpFs = null; }

  // ── Temp folder ──────────────────────────────────────────────────
  async function _getTempBase() {
    if (uxpFs && uxpFs.getTemporaryFolder) {
      try {
        const tmp = await uxpFs.getTemporaryFolder();
        if (tmp && tmp.nativePath) return tmp.nativePath;
      } catch (_) {}
    }
    try {
      const e = (typeof process !== 'undefined' && process.env) || {};
      if (e.TEMP && e.TEMP.length) return e.TEMP;
      if (e.TMP  && e.TMP.length)  return e.TMP;
      if (e.LOCALAPPDATA) return e.LOCALAPPDATA + '\\Temp';
    } catch (_) {}
    try {
      if (os.homedir) {
        const h = os.homedir();
        if (h) return h + '\\AppData\\Local\\Temp';
      }
    } catch (_) {}
    throw new Error('Cannot find writable temp folder');
  }

  Import._tempPath = async function (safeName) {
    const base = await _getTempBase();
    return path.join(base, 'dragonflight-' + safeName);
  };

  // Returns true if the path already exists on disk.
  Import._fileExists = async function (filePath) {
    try { await fs.stat(filePath); return true; } catch (_) { return false; }
  };

  // Write ArrayBuffer to disk via fs.writeFile.
  // If the file is locked (EBUSY — Premiere already has it open from a
  // previous import) we treat that as success: the bytes are already there.
  Import._writeBuffer = async function (destPath, arrayBuffer) {
    try {
      await fs.writeFile(destPath, arrayBuffer);
    } catch (e) {
      const busy = e.code === 'EBUSY' || /resource busy/i.test(String(e.message));
      if (!busy) throw e;
      // File locked by Premiere — it's already there, proceed.
      console.warn('[df] _writeBuffer EBUSY on', destPath, '— using existing file');
    }
    return destPath;
  };

  // Fetch — no redirect option (not supported in UXP)
  // addAuth: only for same-origin proxy URLs, NOT for S3 presigned URLs
  async function _fetch(url, addAuth) {
    const headers = {};
    if (addAuth && API.state.apiToken) {
      headers['Authorization'] = 'Bearer ' + API.state.apiToken;
    }
    const r = await fetch(url, { headers });
    if (!r.ok) throw new Error('HTTP ' + r.status + ' fetching ' + url);
    return r;
  }

  // ── premierepro lazy require ─────────────────────────────────────
  function _ppro() {
    if (Import._ppro_mod) return Import._ppro_mod;
    try { Import._ppro_mod = require('premierepro'); }
    catch (e) { throw new Error('UXP premierepro unavailable: ' + e.message); }
    return Import._ppro_mod;
  }

  // Import a file into the active Premiere project.
  // ALL premierepro calls must be awaited — runtime returns Promises
  // even though docs list them as synchronous.
  // importFiles returns false when the file is already in the project — not an error.
  Import.importIntoProject = async function (filePath) {
    const P = _ppro();
    const project = await P.Project.getActiveProject();
    if (!project) throw new Error('No active Premiere project');
    const root = await project.getRootItem();
    await project.importFiles([filePath], true, root, false);
    // Return value false = already in project. Either way, we're done.
    return true;
  };

  // ── Proxy import ─────────────────────────────────────────────────
  Import.proxy = async function (asset) {
    const safeName = UI.sanitizeFilename((asset.display_name || asset.filename || asset.id) + '.mp4');
    const dest     = await Import._tempPath(safeName);

    const alreadyOnDisk = await Import._fileExists(dest);
    if (alreadyOnDisk) {
      // File exists from a previous import — skip download, just (re-)import.
      UI.showProgress('Importing into Premiere…', 92);
    } else {
      UI.showProgress('Resolving proxy URL…', 4);
      const { url } = await API.getProxyUrl(asset.id);

      UI.showProgress('Downloading ' + safeName + '…', 10);
      const r = await _fetch(url, true);   // same-origin — add auth

      UI.showProgress('Writing to disk…', 70);
      const buf = await r.arrayBuffer();
      await Import._writeBuffer(dest, buf);

      UI.showProgress('Importing into Premiere…', 92);
    }

    await Import.importIntoProject(dest);
    UI.hideProgress();
    UI.toast('Imported: ' + safeName, 'ok');
    return { localPath: dest, safeName };
  };

  // ── Hi-Res import ────────────────────────────────────────────────
  Import.hires = async function (asset) {
    UI.showProgress('Resolving hi-res URL…', 4);
    const info     = await API.getHiresInfo(asset.id);
    const safeName = UI.sanitizeFilename(info.filename || (asset.display_name || asset.id) + '.' + (info.ext || 'mxf'));
    const dest     = await Import._tempPath(safeName);

    const alreadyOnDisk = await Import._fileExists(dest);
    if (alreadyOnDisk) {
      UI.showProgress('Importing into Premiere…', 92);
    } else {
      UI.showProgress('Downloading ' + safeName + ' (' + UI.formatBytes(Number(info.file_size || 0)) + ')…', 8);
      const r = await _fetch(info.url, false);  // presigned S3 — no auth

      UI.showProgress('Writing to disk…', 75);
      const buf = await r.arrayBuffer();
      await Import._writeBuffer(dest, buf);

      UI.showProgress('Importing into Premiere…', 92);
    }

    await Import.importIntoProject(dest);
    UI.hideProgress();
    UI.toast('Hi-res imported: ' + safeName, 'ok');
    return { localPath: dest, safeName };
  };

  // Used by timeline.js batch relink
  Import._streamToFile = async function (response, destPath /*, onProgress */) {
    const buf = await response.arrayBuffer();
    await Import._writeBuffer(destPath, buf);
    return destPath;
  };

  // ── Upload (ingest editor media into the MAM) ────────────────────
  const SIMPLE_MAX = 45 * 1024 * 1024;     // server caps /simple at <50 MB
  const PART_SIZE  = 16 * 1024 * 1024;     // chunk size for multipart

  function _contentType(name) {
    const ext = String(name).split('.').pop().toLowerCase();
    const map = {
      mp4:'video/mp4', m4v:'video/mp4', mov:'video/quicktime', mxf:'application/mxf',
      mkv:'video/x-matroska', avi:'video/x-msvideo', mpg:'video/mpeg', mpeg:'video/mpeg',
      mts:'video/mp2t', m2ts:'video/mp2t', wav:'audio/wav', aif:'audio/aiff', aiff:'audio/aiff',
      mp3:'audio/mpeg', png:'image/png', jpg:'image/jpeg', jpeg:'image/jpeg',
    };
    return map[ext] || 'application/octet-stream';
  }

  // Read a local file and push it to the MAM. Returns the created asset row.
  // NOTE: reads the whole file into memory once (fine for typical clips;
  // very large multi-GB originals may strain memory — revisit with a
  // positional-read stream if that becomes a problem).
  Import.uploadFile = async function (nativePath, meta) {
    meta = meta || {};
    if (!meta.projectId) throw new Error('No target project for upload');
    const filename    = meta.filename || path.basename(nativePath);
    const contentType = _contentType(filename);

    const buf  = await fs.readFile(nativePath);
    const size = buf.byteLength != null ? buf.byteLength : buf.length;

    if (size <= SIMPLE_MAX) {
      const blob = new Blob([buf], { type: contentType });
      return API.uploadSimple(blob, { filename, projectId: meta.projectId, binId: meta.binId, contentType });
    }

    // Chunked multipart for large files.
    const init = await API.uploadInit({ filename, fileSize: size, contentType, projectId: meta.projectId, binId: meta.binId });
    const parts = [];
    try {
      let partNumber = 1;
      for (let off = 0; off < size; off += PART_SIZE, partNumber++) {
        const chunk = buf.slice(off, Math.min(off + PART_SIZE, size));
        const blob  = new Blob([chunk], { type: contentType });
        if (meta.onProgress) meta.onProgress(off, size);
        const res = await API.uploadPart(blob, { uploadId: init.uploadId, key: init.key, partNumber });
        parts.push({ PartNumber: partNumber, ETag: res.etag });
      }
      return API.uploadComplete({ uploadId: init.uploadId, key: init.key, assetId: init.assetId, parts });
    } catch (e) {
      await API.uploadAbort({ uploadId: init.uploadId, key: init.key, assetId: init.assetId });
      throw e;
    }
  };

  // ── Bin selection (best-effort) + file-picker fallback ───────────
  // Tries to read the highlighted project-panel item(s). The UXP premierepro
  // selection surface varies by version, so every access is guarded; on any
  // miss this returns [] and callers fall back to a native file picker.
  Import.getSelectedBinPaths = async function () {
    const paths = [];
    try {
      const P = _ppro();
      const project = await P.Project.getActiveProject();
      if (!project) return paths;
      let sel = null;
      try { if (project.getSelection) sel = await project.getSelection(); } catch (_) {}
      let items = [];
      if (sel) {
        if (typeof sel.getItems === 'function')      items = await sel.getItems();
        else if (Array.isArray(sel))                 items = sel;
        else if (Array.isArray(sel.items))           items = sel.items;
      }
      for (const it of (items || [])) {
        try {
          const ci = await P.ClipProjectItem.cast(it);
          const mp = await ci.getMediaFilePath();
          if (mp) paths.push(mp);
        } catch (_) {}
      }
    } catch (_) {}
    return paths;
  };

  // Native file picker — returns array of native paths (may be empty).
  Import.pickFiles = async function () {
    if (!uxpFs || !uxpFs.getFileForOpening) throw new Error('File picker unavailable in this host');
    const sel = await uxpFs.getFileForOpening({ allowMultiple: true });
    if (!sel) return [];
    const arr = Array.isArray(sel) ? sel : [sel];
    return arr.map(f => f && f.nativePath).filter(Boolean);
  };

  // Upload any timeline clips not yet in the MAM, recording the path→asset
  // mapping so resolveClipsToAssets picks them up on the next pass.
  Import.ensureClipsInMam = async function (clips, projectId, onProgress) {
    const missing = clips.filter(c => !c.asset_id && c.filePath);
    for (let i = 0; i < missing.length; i++) {
      const c = missing[i];
      if (onProgress) onProgress(c.fileName || path.basename(c.filePath), i + 1, missing.length);
      const asset = await Import.uploadFile(c.filePath, { projectId, filename: path.basename(c.filePath) });
      if (asset && asset.id) {
        Library.recordImport(c.filePath, { assetId: asset.id });
        if (c.fileName) Library.recordImport('name:' + c.fileName, { assetId: asset.id });
      }
    }
    return { uploaded: missing.length };
  };

  window.Import = Import;
})();
