import { waitForLegacySession } from '../legacy/session-bridge.js';

const DRIVE_CHUNK_ALIGNMENT = 256 * 1024;
const DEFAULT_DRIVE_CHUNK_SIZE = 4 * 1024 * 1024;
const DEFAULT_DROPBOX_CHUNK_SIZE = 8 * 1024 * 1024;

function createAbortError() {
  const error = new Error('Aborted');
  error.name = 'AbortError';
  return error;
}

export class CloudUploadCoordinator {
  constructor(session) {
    this.session = session;
  }

  static async create() {
    const session = await waitForLegacySession();
    return new CloudUploadCoordinator(session);
  }

  ensureDriveClient() {
    if (!this.session.gdrive && typeof window.setupGoogleDriveUploader === 'function') {
      this.session.gdrive = window.setupGoogleDriveUploader();
    }
    return this.session.gdrive || null;
  }

	async ensureDropboxClient(token, options = {}) {
		let opts = options;
		if (typeof token === 'object' && token !== null && !Array.isArray(token)) {
			opts = token;
			token = undefined;
		}
		opts = opts || {};
		const forceReauth = Boolean(opts.forceReauth);
		if (typeof token === 'string' && token.trim().length) {
			token = token.trim();
		}
		const manualTokenProvided = typeof token === 'string' && token.length > 0;
		const previousClient = this.session.dbx || null;
		const oauthRecord =
			(this.session && this.session.dropboxOAuth) ||
			(typeof session !== 'undefined' ? session.dropboxOAuth : null) ||
			null;
		const oauthFresh = Boolean(
			oauthRecord && (!oauthRecord.expiresAt || Date.now() < oauthRecord.expiresAt),
		);
		if (!forceReauth && !manualTokenProvided && this.session.dbx && oauthFresh) {
			return this.session.dbx;
		}
	if (typeof window.setupDropbox !== 'function') {
		return this.session.dbx || null;
	}
	try {
		const client = await window.setupDropbox(token, opts);
		if (client) {
			this.session.dbx = client;
			return client;
		}
	} catch (error) {
		if (forceReauth && previousClient && !this.session.dbx) {
			this.session.dbx = previousClient;
		}
		throw error;
	}
	if (!this.session.dbx && previousClient) {
		this.session.dbx = previousClient;
	}
	return this.session.dbx || null;
}

  startDriveUpload(filename, sessionUri) {
    if (typeof window.setupGoogleDriveUploader !== 'function') {
      throw new Error('Google Drive uploader is not available in this build.');
    }
    return window.setupGoogleDriveUploader(filename, sessionUri);
  }

  createDriveChunkWriter(filename, sessionUri) {
    const uploader = this.startDriveUpload(filename, sessionUri);
    return {
      addChunk: (chunk) => uploader?.addChunk?.(chunk),
      finalize: () => uploader?.finalize?.(),
      uploader,
    };
  }

  createDropboxChunkWriter(filename) {
    if (typeof window.streamVideoToDropbox !== 'function') {
      throw new Error('Dropbox uploader is not available in this build.');
    }
    return window.streamVideoToDropbox(filename);
  }

  hasDriveAccess() {
    return Boolean(this.session.gdrive && this.session.gdrive.accessToken);
  }

  hasDropboxAccess() {
    return Boolean(this.session.dbx);
  }

  async uploadBlob(blob, options = {}) {
    if (!blob) {
      throw new Error('uploadBlob expects a Blob.');
    }
    const {
      filename,
      drive = true,
      dropbox = true,
      onProgress,
      signal,
      driveChunkSize = DEFAULT_DRIVE_CHUNK_SIZE,
      dropboxChunkSize = DEFAULT_DROPBOX_CHUNK_SIZE,
    } = options;

    const results = {};

    if (drive) {
      try {
        results.drive = await this.uploadBlobToDrive(blob, {
          filename,
          onProgress,
          signal,
          chunkSize: driveChunkSize,
        });
      } catch (error) {
        results.drive = { status: 'error', service: 'drive', error };
      }
    } else {
      results.drive = { status: 'skipped', service: 'drive', reason: 'disabled' };
    }

    if (dropbox) {
      try {
        results.dropbox = await this.uploadBlobToDropbox(blob, {
          filename,
          onProgress,
          signal,
          chunkSize: dropboxChunkSize,
        });
      } catch (error) {
        results.dropbox = { status: 'error', service: 'dropbox', error };
      }
    } else {
      results.dropbox = { status: 'skipped', service: 'dropbox', reason: 'disabled' };
    }

    return results;
  }

  async uploadBlobToDrive(blob, { filename, onProgress, signal, chunkSize = DEFAULT_DRIVE_CHUNK_SIZE } = {}) {
    const client = this.ensureDriveClient();
    if (!client) {
      return { status: 'skipped', service: 'drive', reason: 'unavailable' };
    }
    if (signal?.aborted) {
      throw createAbortError();
    }
    const name = filename || `recording-${Date.now()}.wav`;
    let writer;
    try {
      writer = this.createDriveChunkWriter(name, this.session.gdrive?.sessionUri);
    } catch (error) {
      return { status: 'error', service: 'drive', error };
    }
    if (!writer?.addChunk) {
      return { status: 'error', service: 'drive', error: new Error('Drive writer unavailable') };
    }
    const total = blob.size || 0;
    const alignment = DRIVE_CHUNK_ALIGNMENT;
    const adjustedChunkSize = Math.max(alignment, Math.floor(chunkSize / alignment) * alignment);
    let uploaded = 0;
    for (let offset = 0; offset < total; offset += adjustedChunkSize) {
      if (signal?.aborted) {
        throw createAbortError();
      }
      const chunk = blob.slice(offset, Math.min(total, offset + adjustedChunkSize), blob.type || 'application/octet-stream');
      writer.addChunk(chunk);
      uploaded += chunk.size;
      if (typeof onProgress === 'function') {
        onProgress({
          service: 'drive',
          uploaded,
          total,
          percentage: total ? Math.min(100, Math.round((uploaded / total) * 100)) : 0,
        });
      }
    }
    writer.addChunk(false);
    if (typeof writer.finalize === 'function') {
      try {
        await writer.finalize();
      } catch (error) {
        return { status: 'error', service: 'drive', error };
      }
    }
    return { status: 'uploaded', service: 'drive', filename: name, bytes: uploaded };
  }

  async uploadBlobToDropbox(blob, { filename, onProgress, signal, chunkSize = DEFAULT_DROPBOX_CHUNK_SIZE } = {}) {
    const client = await this.ensureDropboxClient();
    if (!client) {
      return { status: 'skipped', service: 'dropbox', reason: 'unavailable' };
    }
    if (signal?.aborted) {
      throw createAbortError();
    }
    let writer;
    const name = filename || `recording-${Date.now()}.wav`;
    try {
      writer = await this.createDropboxChunkWriter(name);
    } catch (error) {
      return { status: 'error', service: 'dropbox', error };
    }
    if (typeof writer !== 'function') {
      return { status: 'error', service: 'dropbox', error: new Error('Dropbox writer unavailable') };
    }
    const total = blob.size || 0;
    let uploaded = 0;
    for (let offset = 0; offset < total; offset += chunkSize) {
      if (signal?.aborted) {
        throw createAbortError();
      }
      const chunk = blob.slice(offset, Math.min(total, offset + chunkSize), blob.type || 'application/octet-stream');
      await writer(chunk);
      uploaded += chunk.size;
      if (typeof onProgress === 'function') {
        onProgress({
          service: 'dropbox',
          uploaded,
          total,
          percentage: total ? Math.min(100, Math.round((uploaded / total) * 100)) : 0,
        });
      }
    }
    await writer(false);
    return { status: 'uploaded', service: 'dropbox', filename: name, bytes: uploaded };
  }
}
