package maven

import (
	"encoding/json"
	"fmt"
	"os"
)

type lockFile interface {
	ListDependencies() []string
	GetDependencyCoordinates(name string) string
	ListDependencyPackages(name string) []string
}

type versionnedConfigFile struct {
	// Version 0.1.0 has a version inside
	DependencyTree lockFileV1_dependencyTree `json:"dependency_tree"`
	//  Version 2 has a version at the top level
	Version string
}

type lockFileV1 struct {
	DependencyTree lockFileV1_dependencyTree `json:"dependency_tree"`
}

type lockFileV1_dependencyTree struct {
	ConflictResolution map[string]string               `json:"conflict_resolution"`
	Dependencies       []lockFileV1_dependencyTree_dep `json:"dependencies"`
	Version            string                          `json:"version"`
}

type lockFileV1_dependencyTree_dep struct {
	Coord              string   `json:"coord"`
	Dependencies       []string `json:"dependencies"`
	DirectDependencies []string `json:"directDependencies"`
	File               string   `json:"file"`
	MirrorUrls         []string `json:"mirror_urls,omitempty"`
	Packages           []string `json:"packages"`
	Sha256             string   `json:"sha256,omitempty"`
	URL                string   `json:"url,omitempty"`
	Exclusions         []string `json:"exclusions,omitempty"`
}

func (f *lockFileV1) ListDependencies() []string {
	var out []string
	for _, dep := range f.DependencyTree.Dependencies {
		out = append(out, dep.Coord)
	}
	return out
}

func (f *lockFileV1) GetDependencyCoordinates(name string) string {
	return name
}

func (f *lockFileV1) ListDependencyPackages(name string) []string {
	for _, dep := range f.DependencyTree.Dependencies {
		if dep.Coord == name {
			return dep.Packages
		}
	}
	panic(fmt.Sprintf("did not find package information for %s", name))
}

type lockFileV2 struct {
	AutogeneratedFileDoNotModifyThisFileManually string                         `json:"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY"`
	InputArtifactsHash                           int                            `json:"__INPUT_ARTIFACTS_HASH"`
	ResolvedArtifactsHash                        int                            `json:"__RESOLVED_ARTIFACTS_HASH"`
	Artifacts                                    map[string]lockFileV2_Artifact `json:"artifacts"`
	Dependencies                                 map[string][]string            `json:"dependencies"`
	Packages                                     map[string][]string            `json:"packages"`
	Repositories                                 map[string][]string            `json:"repositories"`
	Version                                      string                         `json:"version"`
}

func (f *lockFileV2) ListDependencies() []string {
	var out []string
	for name := range f.Artifacts {
		out = append(out, name)
	}
	return out
}

func (f *lockFileV2) GetDependencyCoordinates(name string) string {
	return name + ":" + f.Artifacts[name].Version
}

func (f *lockFileV2) ListDependencyPackages(name string) []string {
	return f.Packages[name]
}

type lockFileV2_Artifact struct {
	Shasums map[string]string `json:"shasums"`
	Version string            `json:"version"`
}

func loadConfiguration(filename string) (lockFile, error) {
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	var versionFile versionnedConfigFile
	if err := json.Unmarshal(data, &versionFile); err != nil {
		return nil, err
	}

	// Identify the v2 lockfile following
	// https://github.com/bazelbuild/rules_jvm_external/blob/c0436bd77c94b19004436ed4be7a9cf25af7f1a2/private/rules/v2_lock_file.bzl#L16-L27
	if versionFile.Version == "2" {
		var c lockFileV2
		if err := json.Unmarshal(data, &c); err != nil {
			return nil, err
		}
		return &c, nil
	}

	// Identify the v0.1.0 lockfile following
	// https://github.com/bazelbuild/rules_jvm_external/blob/c0436bd77c94b19004436ed4be7a9cf25af7f1a2/private/rules/v1_lock_file.bzl#L16-L32
	if versionFile.DependencyTree.Version == "0.1.0" {
		var c lockFileV1
		if err := json.Unmarshal(data, &c); err != nil {
			return nil, err
		}
		return &c, nil
	}

	return nil, fmt.Errorf("unknown version %#v", versionFile)
}
