/**
 * @module utils/download
 * @description Utilities for downloading files from URLs
 */

import axios from "axios";
import fs from "fs";
import path from "path";
import { createLogger } from "./logger.js";
import { ResponseType } from "../types/index.js";

const logger = createLogger("download");

/**
 * Downloads a file from a URL
 *
 * @param url - The URL to download from
 * @param responseType - The axios response type
 * @returns The response data, type varies based on responseType
 *
 * @example
 * ```typescript
 * // Download a file as an arraybuffer
 * const data = await downloadFile('https://example.com/file.zip');
 *
 * // Download a file as JSON
 * const jsonData = await downloadFile('https://api.example.com/data', 'json');
 * ```
 *
 * @throws Will throw an error if the download fails
 */
export async function downloadFile(
  url: string,
  responseType: ResponseType = "arraybuffer"
): Promise<any> {
  try {
    logger.info(`Downloading from: ${url}`);
    const response = await axios.get(url, { responseType });
    logger.success("Download complete");
    return response.data;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      if (error.response) {
        logger.error(`Download failed with status: ${error.response.status}`);
      } else if (error.request) {
        logger.error("No response received from server");
      } else {
        logger.error(`Error setting up request: ${error.message}`);
      }
    } else {
      logger.error(`Unexpected error: ${(error as Error).message}`);
    }
    throw error;
  }
}

/**
 * Downloads a file from a URL and saves it to disk
 *
 * @param url - The URL to download from
 * @param outputPath - The path to save the file to
 * @returns A promise that resolves when the file is saved
 *
 * @example
 * ```typescript
 * // Download and save a file
 * await downloadAndSaveFile('https://example.com/file.zip', './downloads/file.zip');
 * ```
 *
 * @throws Will throw an error if the download or save fails
 */
export async function downloadAndSaveFile(
  url: string,
  outputPath: string
): Promise<void> {
  try {
    logger.info(`Downloading ${url} to ${outputPath}`);
    const response = await axios({
      method: "get",
      url,
      responseType: "stream",
    });

    // Ensure directory exists
    const dir = path.dirname(outputPath);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }

    // Save the file
    const writer = fs.createWriteStream(outputPath);
    response.data.pipe(writer);

    return new Promise((resolve, reject) => {
      writer.on("finish", () => {
        logger.success(`File saved to ${outputPath}`);
        resolve();
      });
      writer.on("error", (err: Error) => {
        logger.error(`Failed to save file: ${err.message}`);
        reject(err);
      });
    });
  } catch (error) {
    logger.error(`Download and save failed: ${(error as Error).message}`);
    throw error;
  }
}
