package database

import (
	"fmt"
	"os"
	// "archive/tar"
	"bufio"
	"context"
	"net/http"
	"path"
	"path/filepath"
	"sync"
	"time"
	// "net/url"
	"io"
	"strings"
	
	"qulay/config"
	"codeberg.org/UzbekLinux/uzbekdb-go"

	"github.com/codeclysm/extract/v3"
	// "github.com/klauspost/compress/zstd"
)

var _name string = filepath.Base(os.Args[0])

func GetReposUrls() []string {
	f, err := os.Open(paths.ReposUrlsFile)
	if err != nil {
		return []string{}
	}
	defer f.Close()
	var urls []string
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		urls = append(urls, scanner.Text())
	}
	return urls
}

func DownloadRepos(urls []string, destDir string) {
	var wg sync.WaitGroup
	var links []string

	for _, link := range urls {
		link = strings.TrimSpace(link)
		if link == "" {
			continue
		}
		links = append(links, link)
	}
	wg.Add(len(links))
	for _, link := range links {
		go func(link string) {
			defer wg.Done()
			archiveName := path.Base(link)
			repoName := strings.TrimSuffix(archiveName, ".tar.zst")
			destArchivePath := filepath.Join(destDir, "tmp", archiveName)
			destNArchivePath := filepath.Join(destDir, "tmp")
			destPath := filepath.Join(destDir, paths.ReposDir)
			fullDestPath := filepath.Join(destDir, paths.ReposDir, repoName)

			os.MkdirAll(destNArchivePath, 0755)

			client := &http.Client{
				Timeout: 10 * time.Second,
			}

			resp, err := client.Get(link)
			if err != nil {
				fmt.Println("!! failed to download", link, err)
				return
			}
			defer resp.Body.Close()

			f, err := os.Create(destArchivePath)
			if err != nil {
				fmt.Println("!! failed to create file", err)
				return
			}
			defer f.Close()

			_, err = io.Copy(f, resp.Body)
			if err != nil {
				fmt.Println("!! failed to copy", err)
				return
			}

			fmt.Println(":: downloaded", archiveName, "to", destArchivePath)

			err = os.RemoveAll(fullDestPath)
			fmt.Println(":: removing old repo", fullDestPath)
			if err != nil {
				fmt.Println("w! failed to delete old repo", fullDestPath, err)
			}

			file, err := os.Open(destArchivePath)
			if err != nil {
				fmt.Println("!! failed to open archive file", destArchivePath, err)
				return
			}
			err = extract.Zstd(context.TODO(), file, destPath, nil)
			if err != nil {
				fmt.Println("!! failed to extract", err)
				return
			}
		}(link)
	}
	wg.Wait()
	fmt.Println(":: repo updating ended")
}

func ReadInstalled(destDir string) map[interface{}][]interface{} {
	installedFullPath := filepath.Join(destDir, paths.InstalledFile)
	file, err := os.OpenFile(installedFullPath, os.O_RDONLY, 0644)
	if err != nil {
		return map[interface{}][]interface{}{}
	}
	defer file.Close()
	text, err := io.ReadAll(file)
	if err != nil {
		return map[interface{}][]interface{}{}
	}
	data := uzbekdb.Loads(string(text), "map")
	if data == nil {
	    return map[interface{}][]interface{}{}
	}
	return data.(map[interface{}][]interface{})
}

func WriteInstalled(data map[interface{}][]interface{}, destDir string) error {
	installedFullPath := filepath.Join(destDir, paths.InstalledFile)
	file, err := os.OpenFile(installedFullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(uzbekdb.Dumps(data))
	return err
}

func IsInstalled(name string, destDir string) bool {
	data := ReadInstalled(destDir)
	if !strings.Contains(name, "/") {
		return false
	}
	_, ok := data[name]
	return ok
}

func GetPackageVersion(name string, destDir string) string {
	data := ReadInstalled(destDir)
	alo, _ := data[name]
	if len(alo) > 0 {
		return alo[0].(string)
	} else {
		return ""
	}
}

func RegPackage(name string, version string, destDir string) error {
	data := ReadInstalled(destDir)
	_, installed := data[name]
	if installed {
		if data[name][0].(string) != version {
			data[name][0] = version
		}
	} else {
		data[name] = []interface{}{version}
	}
	return WriteInstalled(data, destDir)
}

func UnRegPackage(name string, destDir string) error {
	data := ReadInstalled(destDir)
	_, ok := data[name]
	if !ok {
		return fmt.Errorf("!! cant unregister '%s': package", name, "is not installed")
	}
	delete(data, name)
	return WriteInstalled(data, destDir)
}

func PurgeRepository(name string, destDir string) error {
	dirPath := filepath.Join(destDir, paths.ReposDir, name)
	err := os.RemoveAll(dirPath)
	if err != nil {
		return err
	}
	return nil
}

// func Ensure(args, destDir) error {
// 	
// }
