package spm

import (
	"fmt"
	"path/filepath"
)

// pendingUpdates holds info about packages that have been downloaded/decompressed
// but not yet moved to the final install location, cuz Windows has this stupid file locking mechanism
var pendingUpdates []AppIndexEntry

// DownloadUpdates downloads the APPINDEX file, parses it, compares against
// currently installed packages, and if it finds a newer version, downloads
// and decompresses it into a temporary folder. The result is stored in pendingUpdates, so it can be used by InstallUpdates().
func DownloadUpdates() error {
	// 1) Download the APPINDEX file to a temporary location
	err := UpdateIndex()
	if err != nil {
		return fmt.Errorf("[ERROR] Failed to download APPINDEX: %w", err)
	}
	fmt.Println("[INFO] APPINDEX downloaded successfully")

	// 2) Parse the APPINDEX file
	entries, err := GetIndex()
	if err != nil {
		return fmt.Errorf("[ERROR] Failed to parse APPINDEX: %w", err)
	}
	fmt.Printf("[INFO] Parsed APPINDEX successfully, found %d entries\n", len(entries))

	// 3) Load installed packages
	fmt.Println("[INFO] Loading installed packages")
	installDir, err := GetInstallDir()
	if err != nil {
		return err
	}
	fmt.Println("[INFO] Install directory:", installDir)

	installedPkgs, err := loadInstalledPackages(installDir)
	if err != nil {
		return fmt.Errorf("[ERROR] Failed to load installed packages: %w", err)
	}
	fmt.Printf("[INFO] Loaded %d installed packages\n", len(installedPkgs))

	// 4) Process entries for installed packages only
	for _, installed := range installedPkgs {
		fmt.Printf("[INFO] Checking updates for installed package: %+v\n", installed)

		// Filter APPINDEX entries that match the installed package's attributes
		var matchingEntry *AppIndexEntry
		for _, entry := range entries {
			if entry.Name == installed.Name &&
				entry.Release == installed.Release &&
				entry.Type == installed.Type &&
				entry.OS == installed.OS &&
				entry.Arch == installed.Arch {
				matchingEntry = &entry
				break
			}
		}

		if matchingEntry == nil {
			fmt.Printf("[WARN] No matching APPINDEX entry found for installed package: %s (%s)\n", installed.Name, installed.Release)
			continue
		}

		fmt.Printf("[INFO] Found matching APPINDEX entry: %+v\n", *matchingEntry)

		// Determine if an update is needed
		updateNeeded, err := IsUpdateNeeded(installDir, matchingEntry.Name, matchingEntry.Release, matchingEntry.Version, matchingEntry.Arch, matchingEntry.OS)
		if err != nil {
			return fmt.Errorf("[ERROR] Failed to check if update is needed for %s: %w", matchingEntry.Name, err)
		}

		if !updateNeeded {
			fmt.Printf("[INFO] No update needed for package '%s'\n", matchingEntry.Name)
			continue
		}

		// 5) Download the package into a temporary download folder
		downloadDir := GetTempDir()
		fmt.Printf("[INFO] Downloading package '%s' to temporary folder: %s\n", matchingEntry.Name, downloadDir)

		err = DownloadPackageFromAppIndex(matchingEntry.Name, matchingEntry.Release, matchingEntry.Type, downloadDir)
		if err != nil {
			return fmt.Errorf("[ERROR] Failed to download package '%s': %w", matchingEntry.Name, err)
		}

		fmt.Printf("[INFO] Package '%s' downloaded successfully to: %s\n", matchingEntry.Name, downloadDir)

		// 6) Decompress the package into another temp folder
		fmt.Printf("[INFO] Decompressing package '%s'\n", matchingEntry.Name)
		tempDir, err := DecompressPackage(downloadDir, matchingEntry.Name, matchingEntry.Arch, matchingEntry.OS, matchingEntry.Type, matchingEntry.Release, matchingEntry.Version)
		if err != nil {
			return fmt.Errorf("[ERROR] Failed to decompress package '%s': %w", matchingEntry.Name, err)
		}
		fmt.Printf("[INFO] Package '%s' decompressed successfully to: %s\n", matchingEntry.Name, tempDir)

		// 7) Store in pendingUpdates so that InstallUpdates can finish the job
		fmt.Printf("[INFO] Adding '%s' to pending updates\n", matchingEntry.Name)
		pendingUpdates = append(pendingUpdates, AppIndexEntry{
			Name:    matchingEntry.Name,
			Version: matchingEntry.Version,
			Release: matchingEntry.Release,
			Arch:    matchingEntry.Arch,
			OS:      matchingEntry.OS,
			Type:    matchingEntry.Type,
		})
	}

	fmt.Println("[INFO] DownloadUpdates completed successfully")
	return nil
}

// InstallUpdates installs any packages that were downloaded and decompressed by DownloadUpdates.
// It moves files from their temp directories to the final location and updates installed.ini.
func InstallUpdates() error {
	installDir, err := GetInstallDir()
	if err != nil {
		return err
	}

	for _, entry := range pendingUpdates {
		// 1) Construct the same .tar.gz name we used when decompressing
		fileName := fmt.Sprintf("%s@%s@%s@%s@%s@%s",
			entry.Name,    // no 'packageName'
			entry.Arch,    // matches 'arch'
			entry.OS,      // matches 'os'
			entry.Type,    // matches 'type'
			entry.Release, // matches 'release'
			entry.Version, // matches 'version'
		)

		// 3) Combine with your global temp dir
		tempBase := GetTempDir() // e.g. C:\Users\YourUser\AppData\Local\Temp\spm_temp_164326
		decompressedDir := filepath.Join(tempBase, fileName)

		// 4) Move files from that decompressedDir
		fmt.Printf("[INFO] Installing %s from %s\n", entry.Name, decompressedDir)
		err := MoveFilesToInstallDir(decompressedDir, installDir, entry.Type)
		if err != nil {
			return fmt.Errorf("failed to move files for %s: %w", entry.Name, err)
		}

		// 5) Finalize
		err = finalizeInstall(entry.Name, entry.Release, entry.Version, entry.Arch, entry.OS)
		if err != nil {
			return fmt.Errorf("failed to finalize install for %s: %w", entry.Name, err)
		}
	}

	pendingUpdates = nil
	return nil
}

func DownloadSpecified(specs []AppIndexEntry) error {
	// 1) Download the APPINDEX file to a temporary location
	if err := UpdateIndex(); err != nil {
		return fmt.Errorf("[ERROR] Failed to download APPINDEX: %w", err)
	}
	fmt.Println("[INFO] APPINDEX downloaded successfully")

	// 2) Parse the APPINDEX file
	entries, err := GetIndex()
	if err != nil {
		return fmt.Errorf("[ERROR] Failed to parse APPINDEX: %w", err)
	}
	fmt.Printf("[INFO] Parsed APPINDEX successfully, found %d entries\n", len(entries))

	// 3) Get install directory to check for updates
	installDir, err := GetInstallDir()
	if err != nil {
		return err
	}
	fmt.Println("[INFO] Install directory:", installDir)

	// 4) For each item in the passed specs, attempt to download if update is needed
	for _, spec := range specs {
		fmt.Printf("[INFO] Checking requested package: %+v\n", spec)

		// Find matching entry from the parsed APPINDEX
		var matchingEntry *AppIndexEntry
		for _, e := range entries {
			if e.Name == spec.Name &&
				e.Release == spec.Release &&
				e.Type == spec.Type &&
				e.OS == spec.OS &&
				e.Arch == spec.Arch {
				matchingEntry = &e
				break
			}
		}

		if matchingEntry == nil {
			fmt.Printf("[WARN] No matching APPINDEX entry found for package: %s (%s)\n", spec.Name, spec.Release)
			continue
		}
		fmt.Printf("[INFO] Found matching APPINDEX entry: %+v\n", *matchingEntry)

		updateNeeded, err := IsUpdateNeeded(
			installDir,
			matchingEntry.Name,
			matchingEntry.Release,
			matchingEntry.Version,
			matchingEntry.Arch,
			matchingEntry.OS,
		)
		if err != nil {
			return fmt.Errorf("[ERROR] Failed to check if update is needed for %s: %w", matchingEntry.Name, err)
		}

		if !updateNeeded {
			fmt.Printf("[INFO] No update needed for package '%s'\n", matchingEntry.Name)
			continue
		}

		// 5) Download the package
		downloadDir := GetTempDir()
		fmt.Printf("[INFO] Downloading package '%s' to temporary folder: %s\n", matchingEntry.Name, downloadDir)
		if err := DownloadPackageFromAppIndex(
			matchingEntry.Name,
			matchingEntry.Release,
			matchingEntry.Type,
			downloadDir,
		); err != nil {
			return fmt.Errorf("[ERROR] Failed to download package '%s': %w", matchingEntry.Name, err)
		}
		fmt.Printf("[INFO] Package '%s' downloaded successfully to: %s\n", matchingEntry.Name, downloadDir)

		// 6) Decompress the package
		fmt.Printf("[INFO] Decompressing package '%s'\n", matchingEntry.Name)
		tempDir, err := DecompressPackage(
			downloadDir,
			matchingEntry.Name,
			matchingEntry.Arch,
			matchingEntry.OS,
			matchingEntry.Type,
			matchingEntry.Release,
			matchingEntry.Version,
		)
		if err != nil {
			return fmt.Errorf("[ERROR] Failed to decompress package '%s': %w", matchingEntry.Name, err)
		}
		fmt.Printf("[INFO] Package '%s' decompressed successfully to: %s\n", matchingEntry.Name, tempDir)

		// Add to pendingUpdates for InstallUpdates
		fmt.Printf("[INFO] Adding '%s' to pending updates\n", matchingEntry.Name)
		pendingUpdates = append(pendingUpdates, *matchingEntry)
	}

	fmt.Println("[INFO] AutoDownloadSpecifiedPackages completed successfully")
	return nil
}