// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"testing"
)

func TestNdkRoot(t *testing.T) {
	home, err := os.MkdirTemp("", "gomobile-test-")
	if err != nil {
		t.Fatal(err)
	}

	homeorig := os.Getenv("ANDROID_HOME")
	os.Unsetenv("ANDROID_HOME")
	ndkhomeorig := os.Getenv("ANDROID_NDK_HOME")
	os.Unsetenv("ANDROID_NDK_HOME")

	defer func() {
		os.Setenv("ANDROID_HOME", homeorig)
		os.Setenv("ANDROID_NDK_HOME", ndkhomeorig)
		os.RemoveAll(home)
	}()

	makeMockNDK := func(path, version, platforms, abis string) string {
		dir := filepath.Join(home, path)
		if err := os.Mkdir(dir, 0755); err != nil {
			t.Fatalf("couldn't mkdir %q", dir)
		}
		propertiesPath := filepath.Join(dir, "source.properties")
		propertiesData := []byte("Pkg.Revision = " + version)
		if err := os.WriteFile(propertiesPath, propertiesData, 0644); err != nil {
			t.Fatalf("couldn't write source.properties: %v", err)
		}
		metaDir := filepath.Join(dir, "meta")
		if err := os.Mkdir(metaDir, 0755); err != nil {
			t.Fatalf("couldn't mkdir %q", metaDir)
		}
		platformsPath := filepath.Join(metaDir, "platforms.json")
		platformsData := []byte(platforms)
		if err := os.WriteFile(platformsPath, platformsData, 0644); err != nil {
			t.Fatalf("couldn't write platforms.json: %v", err)
		}
		abisPath := filepath.Join(metaDir, "abis.json")
		abisData := []byte(abis)
		if err := os.WriteFile(abisPath, abisData, 0644); err != nil {
			t.Fatalf("couldn't populate abis.json: %v", err)
		}
		return dir
	}

	t.Run("no NDK in the default location", func(t *testing.T) {
		os.Setenv("ANDROID_HOME", home)
		defer os.Unsetenv("ANDROID_HOME")
		if ndk, err := ndkRoot(); err == nil {
			t.Errorf("expected error but got %q", ndk)
		}
	})

	t.Run("NDK location is set but is wrong", func(t *testing.T) {
		os.Setenv("ANDROID_NDK_HOME", filepath.Join(home, "no-such-path"))
		defer os.Unsetenv("ANDROID_NDK_HOME")
		if ndk, err := ndkRoot(); err == nil {
			t.Errorf("expected error but got %q", ndk)
		}
	})

	t.Run("Two NDKs installed", func(t *testing.T) {
		// Default path for pre-side-by-side NDKs.
		sdkNDK := makeMockNDK("ndk-bundle", "fake-version", `{"min":16,"max":32}`, "{}")
		defer os.RemoveAll(sdkNDK)
		// Arbitrary location for testing ANDROID_NDK_HOME.
		envNDK := makeMockNDK("custom-location", "fake-version", `{"min":16,"max":32}`, "{}")

		// ANDROID_NDK_HOME is sufficient.
		os.Setenv("ANDROID_NDK_HOME", envNDK)
		if ndk, err := ndkRoot(); ndk != envNDK {
			t.Errorf("got (%q, %v) want (%q, nil)", ndk, err, envNDK)
		}

		// ANDROID_NDK_HOME takes precedence over ANDROID_HOME.
		os.Setenv("ANDROID_HOME", home)
		if ndk, err := ndkRoot(); ndk != envNDK {
			t.Errorf("got (%q, %v) want (%q, nil)", ndk, err, envNDK)
		}

		// ANDROID_NDK_HOME is respected even if there is no NDK there.
		os.RemoveAll(envNDK)
		if ndk, err := ndkRoot(); err == nil {
			t.Errorf("expected error but got %q", ndk)
		}

		// ANDROID_HOME is used if ANDROID_NDK_HOME is not set.
		os.Unsetenv("ANDROID_NDK_HOME")
		if ndk, err := ndkRoot(); ndk != sdkNDK {
			t.Errorf("got (%q, %v) want (%q, nil)", ndk, err, envNDK)
		}
	})

	t.Run("Modern 'side-by-side' NDK selection", func(t *testing.T) {
		defer func() {
			buildAndroidAPI = minAndroidAPI
		}()

		ndkForest := filepath.Join(home, "ndk")
		if err := os.Mkdir(ndkForest, 0755); err != nil {
			t.Fatalf("couldn't mkdir %q", ndkForest)
		}

		path := filepath.Join("ndk", "newer")
		platforms := `{"min":19,"max":32}`
		abis := `{"arm64-v8a": {}, "armeabi-v7a": {}, "x86_64": {}}`
		version := "17.2.0"
		newerNDK := makeMockNDK(path, version, platforms, abis)

		path = filepath.Join("ndk", "older")
		platforms = `{"min":16,"max":31}`
		abis = `{"arm64-v8a": {}, "armeabi-v7a": {}, "x86": {}}`
		version = "17.1.0"
		olderNDK := makeMockNDK(path, version, platforms, abis)

		testCases := []struct {
			api         int
			targets     []targetInfo
			wantNDKRoot string
		}{
			{15, nil, ""},
			{16, nil, olderNDK},
			{16, []targetInfo{{"android", "arm"}}, olderNDK},
			{16, []targetInfo{{"android", "arm"}, {"android", "arm64"}}, olderNDK},
			{16, []targetInfo{{"android", "x86_64"}}, ""},
			{19, nil, newerNDK},
			{19, []targetInfo{{"android", "arm"}}, newerNDK},
			{19, []targetInfo{{"android", "arm"}, {"android", "arm64"}, {"android", "386"}}, olderNDK},
			{32, nil, newerNDK},
			{32, []targetInfo{{"android", "arm"}}, newerNDK},
			{32, []targetInfo{{"android", "386"}}, ""},
		}

		for i, tc := range testCases {
			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
				buildAndroidAPI = tc.api
				ndk, err := ndkRoot(tc.targets...)
				if len(tc.wantNDKRoot) != 0 {
					if ndk != tc.wantNDKRoot || err != nil {
						t.Errorf("got (%q, %v), want (%q, nil)", ndk, err, tc.wantNDKRoot)
					}
				} else if err == nil {
					t.Error("expected error")
				}
			})
		}
	})
}
