package qbittorrent

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"os"
	"os/exec"
	"path"
	"runtime"
	"time"

	"github.com/google/go-github/v63/github"
	"golang.org/x/sys/cpu"
)

const (
	repoOwner = "userdocs"
	repoName  = "qbittorrent-nox-static"
)

func runQBittorrent(binPath string, profileDir string, port int, stdout, stderr io.Writer) (*os.Process, error) {
	err := os.Chmod(binPath, 0755)
	if err != nil {
		return nil, err
	}

	cmd := exec.Command(binPath, fmt.Sprintf("--profile=%s", profileDir), fmt.Sprintf("--webui-port=%d", port))
	cmd.Stdin = bytes.NewReader([]byte("y\n"))
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err = cmd.Start()
	if err != nil {
		return nil, err
	}
	return cmd.Process, nil
}

func downloadLatestQbitRelease(ctx context.Context, binPath string) error {
	client := github.NewClient(nil)
	rel, _, err := client.Repositories.GetLatestRelease(ctx, repoOwner, repoName)
	if err != nil {
		return err
	}

	arch := ""
	switch runtime.GOARCH {
	case "amd64":
		arch = "x86_64"
	case "arm":
		arch = "armhf" // this is a safe version, go does not distinguish between armv6 and armv7
		if cpu.ARM.HasNEON {
			arch = "armv7"
		}
	case "arm64":
		arch = "aarch64"
	}

	if arch == "" {
		return errors.New("unsupported architecture")
	}

	binName := arch + "-qbittorrent-nox"

	var targetRelease *github.ReleaseAsset
	for _, v := range rel.Assets {
		if v.GetName() == binName {
			targetRelease = v
			break
		}
	}
	if targetRelease == nil {
		return fmt.Errorf("target asset %s not found", binName)
	}

	downloadUrl := targetRelease.GetBrowserDownloadURL()
	if downloadUrl == "" {
		return errors.New("download url is empty")
	}

	err = os.MkdirAll(path.Dir(binPath), 0755)
	if err != nil {
		return err
	}

	slog.InfoContext(ctx, "downloading latest qbittorrent-nox release", slog.String("url", downloadUrl))

	return downloadFile(ctx, binPath, downloadUrl)
}

func downloadFile(ctx context.Context, filepath string, webUrl string) error {
	if stat, err := os.Stat(filepath); err == nil {
		resp, err := http.Head(webUrl)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		var lastModified time.Time

		lastModifiedHeader := resp.Header.Get("Last-Modified")
		if lastModifiedHeader != "" {
			lastModified, err = time.Parse(http.TimeFormat, lastModifiedHeader)
			if err != nil {
				return err
			}
		}

		if resp.ContentLength == stat.Size() && lastModified.Before(stat.ModTime()) {
			slog.InfoContext(ctx, "there is already newest version of the file", slog.String("filepath", filepath))
			return nil
		}
	}

	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, webUrl, nil)
	if err != nil {
		return err
	}

	// Get the data
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

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

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}