package infra

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

// HttpDownloadFile descarga url en destPath en streaming con io.Copy.
// Crea directorios intermedios con os.MkdirAll. Usa archivo temporal + rename
// para garantizar atomicidad (no deja archivo corrupto si falla a mitad).
// Retorna los bytes escritos.
func HttpDownloadFile(url, destPath string, headers map[string]string, timeout time.Duration) (int64, error) {
	client := &http.Client{Timeout: timeout}

	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return 0, fmt.Errorf("http_download_file: build request: %w", err)
	}
	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := client.Do(req)
	if err != nil {
		return 0, fmt.Errorf("http_download_file: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		shortURL := url
		if len(shortURL) > 100 {
			shortURL = shortURL[:100]
		}
		return 0, fmt.Errorf("http_download_file: HTTP %d at %q", resp.StatusCode, shortURL)
	}

	dir := filepath.Dir(destPath)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return 0, fmt.Errorf("http_download_file: create dirs: %w", err)
	}

	// Archivo temporal en el mismo directorio para que rename sea atomico
	tmp, err := os.CreateTemp(dir, ".download-*")
	if err != nil {
		return 0, fmt.Errorf("http_download_file: create temp file: %w", err)
	}
	tmpPath := tmp.Name()
	defer func() {
		tmp.Close()
		os.Remove(tmpPath) // no-op si rename tuvo exito
	}()

	n, err := io.Copy(tmp, resp.Body)
	if err != nil {
		return 0, fmt.Errorf("http_download_file: write: %w", err)
	}

	if err := tmp.Close(); err != nil {
		return 0, fmt.Errorf("http_download_file: close temp: %w", err)
	}

	if err := os.Rename(tmpPath, destPath); err != nil {
		return 0, fmt.Errorf("http_download_file: rename: %w", err)
	}

	return n, nil
}
