const { onObjectFinalized } = require('firebase-functions/v2/storage');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const path = require('path');
const os = require('os');
const fs = require('fs');

// These deps must be installed in functions/: ffmpeg-static, fluent-ffmpeg
let ffmpeg;
let ffmpegPath;
try {
  ffmpeg = require('fluent-ffmpeg');
  ffmpegPath = require('ffmpeg-static');
  ffmpeg.setFfmpegPath(ffmpegPath);
} catch (e) {
  console.log(
    'ffmpeg modules not installed yet — deploy after npm i',
    e?.message
  );
}

const bucket = admin.storage().bucket();

exports.transcodePlaybackToHLS = onObjectFinalized(
  {
    region: 'europe-west1',
    memory: '2GiB',
    timeoutSeconds: 540,
    cpu: 2,
  },
  async (event) => {
    const object = event.data;
    const filePath = object?.name || ''; // e.g., musics/{projectId}/source.mp4
    const contentType = object?.contentType || '';

    try {
      // Only process our target path + video files
      const match = filePath.match(/^musics\/(.+?)\/source\.mp4$/i);
      if (!match || !contentType.startsWith('video/')) {
        return;
      }
      const projectId = match[1];
      const tempDir = fs.mkdtempSync(
        path.join(os.tmpdir(), `hls_${projectId}_`)
      );
      const localSource = path.join(tempDir, 'source.mp4');
      const hlsDir = path.join(tempDir, 'hls');
      fs.mkdirSync(hlsDir);

      // Download source
      await bucket.file(filePath).download({ destination: localSource });

      if (!ffmpeg) {
        console.warn('ffmpeg not available; skip transcode');
        return;
      }

      // Transcode to HLS (single rendition for simplicity)
      const masterPath = path.join(hlsDir, 'master.m3u8');
      const segmentPattern = path.join(hlsDir, 'segment_%03d.ts');

      await new Promise((resolve, reject) => {
        ffmpeg(localSource)
          .outputOptions([
            '-profile:v baseline',
            '-level 3.0',
            '-start_number 0',
            '-hls_time 4',
            '-hls_list_size 0',
            '-f hls',
            `-hls_segment_filename ${segmentPattern}`,
          ])
          .output(masterPath)
          .on('end', resolve)
          .on('error', reject)
          .run();
      });

      // Upload all files in hlsDir to Storage under musics/{projectId}/hls/
      const fileNames = fs.readdirSync(hlsDir);
      const destPrefix = `musics/${projectId}/hls/`;

      // Upload all segments first
      const uploads = fileNames
        .filter((n) => n.endsWith('.ts'))
        .map((name) => {
          const local = path.join(hlsDir, name);
          const dest = `${destPrefix}${name}`;
          return bucket.upload(local, {
            destination: dest,
            metadata: {
              contentType: 'video/MP2T',
              cacheControl: 'public, max-age=3600',
            },
          });
        });
      await Promise.all(uploads);

      // Generate signed URLs for segments and rewrite playlist with absolute URLs
      const segmentNames = fileNames.filter((n) => n.endsWith('.ts'));
      const signedSegments = {};
      await Promise.all(
        segmentNames.map(async (name) => {
          const file = bucket.file(`${destPrefix}${name}`);
          const [url] = await file.getSignedUrl({
            action: 'read',
            expires: Date.now() + 1000 * 60 * 60 * 24 * 365,
          });
          signedSegments[name] = url;
        })
      );

      // Rewrite master playlist to use absolute signed segment URLs
      let masterContent = fs.readFileSync(masterPath, 'utf8');
      masterContent = masterContent
        .split(/\r?\n/)
        .map((line) =>
          line.endsWith('.ts') && signedSegments[line]
            ? signedSegments[line]
            : line
        )
        .join('\n');
      const rewrittenMasterLocal = path.join(hlsDir, 'master.abs.m3u8');
      fs.writeFileSync(rewrittenMasterLocal, masterContent, 'utf8');

      // Upload rewritten playlist
      await bucket.upload(rewrittenMasterLocal, {
        destination: `${destPrefix}master.m3u8`,
        metadata: {
          contentType: 'application/vnd.apple.mpegurl',
          cacheControl: 'public, max-age=3600',
        },
      });

      // Compute signed URL for playlist
      const [signedUrl] = await bucket
        .file(`${destPrefix}master.m3u8`)
        .getSignedUrl({
          action: 'read',
          expires: Date.now() + 1000 * 60 * 60 * 24 * 365,
        });

      // Update Firestore project doc with playbackUrl
      await admin.firestore().collection('projects').doc(projectId).set(
        {
          playbackUrl: signedUrl,
          playbackStatus: 'ready',
          updatedAt: admin.firestore.FieldValue.serverTimestamp(),
        },
        { merge: true }
      );

      // Cleanup tmp dir
      try {
        fs.rmSync(tempDir, { recursive: true, force: true });
      } catch (e) {}
    } catch (e) {
      console.error('HLS transcode error', e);
    }
  }
);
