package download

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"

	"github.com/schollz/progressbar/v3"
)

// File downloads a file from the given URL and saves it to the specified path
func File(url, filepath string) error {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return fmt.Errorf("creating file %s: %w", filepath, err)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("downloading from %s: %w", url, err)
	}
	defer resp.Body.Close()

	// Check server response
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status: %s", resp.Status)
	}

	// Get the content length for progress bar
	var bar *progressbar.ProgressBar
	contentLength := resp.ContentLength
	if contentLength > 0 {
		bar = progressbar.NewOptions64(
			contentLength,
			progressbar.OptionSetDescription("Downloading"),
			progressbar.OptionSetWriter(os.Stderr),
			progressbar.OptionShowBytes(true),
			progressbar.OptionSetWidth(50),
			progressbar.OptionThrottle(65*1000*1000), // 65ms
			progressbar.OptionOnCompletion(func() {
				fmt.Fprint(os.Stderr, "\n")
			}),
			progressbar.OptionSpinnerType(14),
		)
	} else {
		// Indeterminate progress bar if we don't know the size
		bar = progressbar.NewOptions(-1,
			progressbar.OptionSetDescription("Downloading"),
			progressbar.OptionSetWriter(os.Stderr),
			progressbar.OptionSetWidth(50),
			progressbar.OptionThrottle(65*1000*1000),
			progressbar.OptionSpinnerType(14),
			progressbar.OptionOnCompletion(func() {
				fmt.Fprint(os.Stderr, "\n")
			}),
		)
	}

	// Create a reader that updates the progress bar
	reader := &ProgressReader{
		Reader: resp.Body,
		bar:    bar,
	}

	// Write the body to file
	_, err = io.Copy(out, reader)
	if err != nil {
		return fmt.Errorf("writing file: %w", err)
	}

	bar.Finish()
	return nil
}

// ProgressReader wraps an io.Reader and updates a progress bar
type ProgressReader struct {
	io.Reader
	bar *progressbar.ProgressBar
}

func (pr *ProgressReader) Read(p []byte) (int, error) {
	n, err := pr.Reader.Read(p)
	pr.bar.Add(n)
	return n, err
}

// GetFileSize returns the size of a file at the given URL without downloading it
func GetFileSize(url string) (int64, error) {
	resp, err := http.Head(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("bad status: %s", resp.Status)
	}

	size := resp.Header.Get("Content-Length")
	if size == "" {
		return 0, nil // Unknown size
	}

	return strconv.ParseInt(size, 10, 64)
}
