// This file is part of the adequate Debian-native package, and is available
// under the Expat license. For the full terms please see debian/copyright.

package main

import (
	"fmt"
	"log"
	"slices"
	"strings"
)

const (
	MISSING_ALTERNATIVE_TAG = "missing-alternative"
)

type altErr struct {
	pkg  string
	vpkg string
}

func (c altErr) Error() string {
	if c == (altErr{}) {
		return ""
	}
	return fmt.Sprintf("%s: %s %s", c.pkg, MISSING_ALTERNATIVE_TAG, c.vpkg)
}

type updateAltsFunc func([]string) ([]string, error)
type altChecker struct {
	uaFunc updateAltsFunc
	emit   bool
}

func newAltChecker(tags tagFilter) altChecker {
	return altChecker{
		uaFunc: runCommand,
		emit:   tags.shouldEmit(MISSING_ALTERNATIVE_TAG),
	}
}

func (cc altChecker) check(pkgProvides map[string]map[string]bool, pkg2files map[string][]string) []altErr {
	if !cc.emit {
		return nil
	}

	vpkgs := []string{"x-window-manager", "x-terminal-emulator"}
	providers := make(map[string][]string)
	for pkg, provides := range pkgProvides {
		for _, vpkg := range vpkgs {
			if provides[vpkg] {
				providers[vpkg] = append(providers[vpkg], pkg)
			}
		}
	}
	if len(providers) == 0 {
		return nil
	}

	var tags []altErr
	altMap := cc.makeAltMap(vpkgs)

	for vpkg, pkgs := range providers {
		registeredPaths := altMap[vpkg]
	FOUND:
		for _, pkg := range pkgs {
			for _, f := range pkg2files[pkg] {
				if slices.Contains(registeredPaths, f) {
					continue FOUND
				}
			}
			tags = append(tags, altErr{
				pkg:  pkg,
				vpkg: vpkg,
			})
		}
	}

	return tags
}

func (cc altChecker) makeAltMap(interestingAlts []string) map[string][]string {
	lines, err := cc.uaFunc([]string{"update-alternatives", "--get-selections"})
	if err != nil {
		log.Printf("W: update-alternatives failed: %s\n", err)
		return nil
	}

	seenAlts := make(map[string]bool)
	for _, line := range lines {
		// sample line:
		// 	cpp                            auto     /usr/bin/cpp
		fields := strings.Fields(line)
		if n := len(fields); n == 0 || n > 3 {
			continue
		}
		seenAlts[fields[0]] = true
	}

	m := make(map[string][]string)
	for _, alt := range interestingAlts {
		if !seenAlts[alt] {
			continue
		}

		alts, err := cc.uaFunc([]string{"update-alternatives", "--list", alt})
		if err != nil {
			log.Printf("W: update-alternatives failed: %s\n", err)
			continue
		}
		m[alt] = append(m[alt], alts...)
	}

	return m
}
