import { Dropbox } from 'dropbox';
import fetch from 'node-fetch';

// Store the current access token in memory
let dropboxAccessToken = null;

/**
 * Refresh the Dropbox access token using the refresh token
 * @returns {Promise<string>} - New access token
 */
const refreshDropboxToken = async () => {
  try {
    console.log('🔄 Refreshing Dropbox access token...');
    
    // Validate required environment variables
    if (!process.env.DROPBOX_APP_KEY) {
      throw new Error('Missing DROPBOX_APP_KEY in environment variables');
    }
    if (!process.env.DROPBOX_APP_SECRET) {
      throw new Error('Missing DROPBOX_APP_SECRET in environment variables');
    }
    if (!process.env.DROPBOX_REFRESH_TOKEN) {
      throw new Error('Missing DROPBOX_REFRESH_TOKEN in environment variables');
    }

    // Use URL-encoded format for OAuth2 token request
    const params = new URLSearchParams();
    params.append('grant_type', 'refresh_token');
    params.append('refresh_token', process.env.DROPBOX_REFRESH_TOKEN);
    params.append('client_id', process.env.DROPBOX_APP_KEY);
    params.append('client_secret', process.env.DROPBOX_APP_SECRET);

    const response = await fetch('https://api.dropbox.com/oauth2/token', {
      method: 'POST',
      body: params.toString(),
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    });

    if (!response.ok) {
      const errorData = await response.text();
      console.error('❌ Token refresh failed:', errorData);
      throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    dropboxAccessToken = data.access_token;
    
    console.log('✅ Dropbox access token refreshed successfully');
    return dropboxAccessToken;
  } catch (error) {
    console.error('❌ Error refreshing Dropbox token:', error.message);
    throw new Error(`Unable to refresh Dropbox access token: ${error.message}`);
  }
};

/**
 * Get a valid Dropbox access token (refresh if needed)
 * @returns {Promise<string>} - Valid access token
 */
const getDropboxAccessToken = async () => {
  if (!dropboxAccessToken) {
    return await refreshDropboxToken();
  }
  return dropboxAccessToken;
};

/**
 * Upload a base64 encoded image to Dropbox and return a public URL
 * @param {string} base64Data - Base64 encoded image data (with or without data:image/... prefix)
 * @param {string} filename - Name for the file to be saved in Dropbox
 * @param {string} folder - Folder path in Dropbox (e.g., '/website-submissions', '/profile-photos')
 * @returns {Promise<string>} - Public URL of the uploaded file
 */
export async function uploadToDropbox(base64Data, filename, folder = '/uploads') {
  try {
    // Remove data URL prefix if present (e.g., data:image/png;base64,)
    const base64Clean = base64Data.includes(',') 
      ? base64Data.split(',')[1] 
      : base64Data;

    // Convert base64 to buffer
    const fileBuffer = Buffer.from(base64Clean, 'base64');

    // Create full path with folder
    const path = `${folder}/${filename}`;

    console.log(`📤 Uploading file to Dropbox: ${path}`);
    console.log(`📦 File size: ${(fileBuffer.length / 1024).toFixed(2)} KB`);

    // Get a valid access token
    const accessToken = await getDropboxAccessToken();
    const dbx = new Dropbox({ accessToken, fetch });

    // Upload file to Dropbox
    const response = await dbx.filesUpload({
      path: path,
      contents: fileBuffer,
      mode: { '.tag': 'overwrite' }, // Overwrite if file exists
      autorename: false,
      mute: false,
    });

    console.log('✅ File uploaded to Dropbox:', response.result.path_display);

    // Create a shared link for the file
    let sharedLink;
    try {
      const linkResponse = await dbx.sharingCreateSharedLinkWithSettings({
        path: response.result.path_lower,
        settings: {
          requested_visibility: { '.tag': 'public' },
          audience: { '.tag': 'public' },
          access: { '.tag': 'viewer' },
        }
      });
      sharedLink = linkResponse.result.url;
    } catch (linkError) {
      // If link already exists, get the existing link
      if (linkError.error?.error['.tag'] === 'shared_link_already_exists') {
        const existingLinks = await dbx.sharingListSharedLinks({
          path: response.result.path_lower,
        });
        if (existingLinks.result.links.length > 0) {
          sharedLink = existingLinks.result.links[0].url;
        }
      } else {
        console.warn('⚠️ Could not create shared link:', linkError.message);
        // Return the file path if sharing fails
        return response.result.path_display;
      }
    }

    // Convert the Dropbox link to a direct download link
    // New Dropbox links format: https://www.dropbox.com/scl/fi/...?rlkey=...&dl=0
    // Old format: https://www.dropbox.com/s/...?dl=0
    // For direct image display, change dl=0 to raw=1
    let publicUrl = sharedLink;
    
    // Replace dl=0 with raw=1 (handles both ?dl=0 and &dl=0)
    if (publicUrl.includes('dl=0')) {
      publicUrl = publicUrl.replace('dl=0', 'raw=1');
    } else if (publicUrl.includes('?')) {
      // If has query params but no dl parameter, add raw=1
      publicUrl = publicUrl + '&raw=1';
    } else {
      // If no query params at all, add ?raw=1
      publicUrl = publicUrl + '?raw=1';
    }

    console.log('✅ Public URL generated:', publicUrl);

    return publicUrl;
  } catch (error) {
    console.error('❌ Error uploading to Dropbox:', error);
    
    // Handle authentication errors - try refreshing token
    if (error?.status === 401 || error?.error?.error_summary?.includes('token')) {
      console.log('🔄 Token expired, refreshing and retrying...');
      dropboxAccessToken = null; // Clear invalid token
      
      // Retry once with a new token
      try {
        const newToken = await getDropboxAccessToken();
        const dbx = new Dropbox({ accessToken: newToken, fetch });
        
        const base64Clean = base64Data.includes(',') ? base64Data.split(',')[1] : base64Data;
        const fileBuffer = Buffer.from(base64Clean, 'base64');
        const path = `${folder}/${filename}`;
        
        const response = await dbx.filesUpload({
          path: path,
          contents: fileBuffer,
          mode: { '.tag': 'overwrite' },
          autorename: false,
          mute: false,
        });
        
        console.log('✅ File uploaded after token refresh');
        
        // Create shared link for the retry
        let retrySharedLink;
        try {
          const linkResponse = await dbx.sharingCreateSharedLinkWithSettings({
            path: response.result.path_lower,
            settings: {
              requested_visibility: { '.tag': 'public' },
              audience: { '.tag': 'public' },
              access: { '.tag': 'viewer' },
            }
          });
          retrySharedLink = linkResponse.result.url;
        } catch (linkError) {
          if (linkError.error?.error['.tag'] === 'shared_link_already_exists') {
            const existingLinks = await dbx.sharingListSharedLinks({
              path: response.result.path_lower,
            });
            if (existingLinks.result.links.length > 0) {
              retrySharedLink = existingLinks.result.links[0].url;
            }
          }
        }
        
        // Convert to direct download URL
        let retryPublicUrl = retrySharedLink || response.result.path_display;
        if (retryPublicUrl.includes('dl=0')) {
          retryPublicUrl = retryPublicUrl.replace('dl=0', 'raw=1');
        } else if (retryPublicUrl.includes('?')) {
          retryPublicUrl = retryPublicUrl + '&raw=1';
        } else if (retryPublicUrl.includes('dropbox.com')) {
          retryPublicUrl = retryPublicUrl + '?raw=1';
        }
        
        return retryPublicUrl;
      } catch (retryError) {
        console.error('❌ Retry failed after token refresh:', retryError);
        throw new Error('Authentication failed: Please check Dropbox credentials');
      }
    }
    
    // Handle permission errors
    if (error?.error?.error?.required_scope) {
      throw new Error(`Missing required permission: ${error.error.error.required_scope}`);
    }
    
    throw new Error(`Failed to upload file to Dropbox: ${error.message}`);
  }
}

/**
 * Upload multiple files to Dropbox
 * @param {Array<{data: string, filename: string}>} files - Array of files to upload
 * @param {string} folder - Folder path in Dropbox
 * @returns {Promise<Array<string>>} - Array of public URLs
 */
export async function uploadMultipleToDropbox(files, folder = '/uploads') {
  try {
    const uploadPromises = files.map(file => 
      uploadToDropbox(file.data, file.filename, folder)
    );
    
    const urls = await Promise.all(uploadPromises);
    return urls;
  } catch (error) {
    console.error('❌ Error uploading multiple files to Dropbox:', error);
    throw new Error(`Failed to upload multiple files to Dropbox: ${error.message}`);
  }
}

/**
 * Delete a file from Dropbox
 * @param {string} pathOrUrl - Full path or shared URL of the file in Dropbox
 * @returns {Promise<boolean>} - True if deleted successfully
 */
export async function deleteFromDropbox(pathOrUrl) {
  try {
    const accessToken = await getDropboxAccessToken();
    const dbx = new Dropbox({ accessToken, fetch });

    let filePath = pathOrUrl;

    // If it's a shared URL, get the file path first
    if (pathOrUrl.includes('dropbox.com')) {
      try {
        const metadata = await dbx.sharingGetSharedLinkMetadata({ url: pathOrUrl });
        filePath = metadata.result.path_lower;
      } catch (metadataError) {
        console.error('⚠️ Could not get metadata from shared URL:', metadataError);
        // Try to use the URL as-is
      }
    }

    // Delete the file using the path
    await dbx.filesDeleteV2({ path: filePath });
    console.log('✅ File deleted from Dropbox:', filePath);
    return true;
  } catch (error) {
    console.error('❌ Error deleting file from Dropbox:', error);
    
    // Handle authentication errors
    if (error?.status === 401) {
      console.log('🔄 Token expired, refreshing and retrying delete...');
      dropboxAccessToken = null;
      
      try {
        const newToken = await getDropboxAccessToken();
        const dbx = new Dropbox({ accessToken: newToken, fetch });
        await dbx.filesDeleteV2({ path: pathOrUrl });
        console.log('✅ File deleted after token refresh');
        return true;
      } catch (retryError) {
        throw new Error('Authentication failed during file deletion');
      }
    }
    
    throw new Error(`Failed to delete file from Dropbox: ${error.message}`);
  }
}

/**
 * Generate a unique filename with timestamp
 * @param {string} originalName - Original filename
 * @param {string} prefix - Prefix for the filename (e.g., 'website', 'profile')
 * @returns {string} - Unique filename
 */
export function generateUniqueFilename(originalName, prefix = 'file') {
  const timestamp = Date.now();
  const randomString = Math.random().toString(36).substring(2, 8);
  const extension = originalName.includes('.') 
    ? originalName.split('.').pop() 
    : 'jpg'; // Default to jpg if no extension
  
  return `${prefix}_${timestamp}_${randomString}.${extension}`;
}

/**
 * Extract file extension from base64 data URL
 * @param {string} base64Data - Base64 data URL
 * @returns {string} - File extension (e.g., 'jpg', 'png')
 */
export function getFileExtensionFromBase64(base64Data) {
  if (base64Data.startsWith('data:image/')) {
    const mimeType = base64Data.split(';')[0].split(':')[1];
    const extension = mimeType.split('/')[1];
    return extension;
  }
  return 'jpg'; // Default extension
}

export default {
  uploadToDropbox,
  uploadMultipleToDropbox,
  deleteFromDropbox,
  generateUniqueFilename,
  getFileExtensionFromBase64
};
