package services_test

import (
	"context"
	"os"
	"path/filepath"

	"github.com/mudler/LocalAI/core/config"
	"github.com/mudler/LocalAI/core/services"
	"github.com/mudler/LocalAI/pkg/model"
	"github.com/mudler/LocalAI/pkg/system"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"gopkg.in/yaml.v3"
)

var _ = Describe("InstallExternalBackend", func() {
	var (
		tempDir     string
		galleries   []config.Gallery
		ml          *model.ModelLoader
		systemState *system.SystemState
	)

	BeforeEach(func() {
		var err error
		tempDir, err = os.MkdirTemp("", "backends-service-test-*")
		Expect(err).NotTo(HaveOccurred())

		systemState, err = system.GetSystemState(system.WithBackendPath(tempDir))
		Expect(err).NotTo(HaveOccurred())
		ml = model.NewModelLoader(systemState)

		// Setup test gallery
		galleries = []config.Gallery{
			{
				Name: "test-gallery",
				URL:  "file://" + filepath.Join(tempDir, "test-gallery.yaml"),
			},
		}
	})

	AfterEach(func() {
		os.RemoveAll(tempDir)
	})

	Context("with gallery backend name", func() {
		BeforeEach(func() {
			// Create a test gallery file with a test backend
			testBackend := []map[string]interface{}{
				{
					"name": "test-backend",
					"uri":  "https://gist.githubusercontent.com/mudler/71d5376bc2aa168873fa519fa9f4bd56/raw/testbackend/run.sh",
				},
			}
			data, err := yaml.Marshal(testBackend)
			Expect(err).NotTo(HaveOccurred())
			err = os.WriteFile(filepath.Join(tempDir, "test-gallery.yaml"), data, 0644)
			Expect(err).NotTo(HaveOccurred())
		})

		It("should fail when name or alias is provided for gallery backend", func() {
			err := services.InstallExternalBackend(
				context.Background(),
				galleries,
				systemState,
				ml,
				nil,
				"test-backend", // gallery name
				"custom-name",  // name should not be allowed
				"",
			)
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("specifying a name or alias is not supported for gallery backends"))
		})

		It("should fail when backend is not found in gallery", func() {
			err := services.InstallExternalBackend(
				context.Background(),
				galleries,
				systemState,
				ml,
				nil,
				"non-existent-backend",
				"",
				"",
			)
			Expect(err).To(HaveOccurred())
		})
	})

	Context("with OCI image", func() {
		It("should fail when name is not provided for OCI image", func() {
			err := services.InstallExternalBackend(
				context.Background(),
				galleries,
				systemState,
				ml,
				nil,
				"oci://quay.io/mudler/tests:localai-backend-test",
				"", // name is required for OCI images
				"",
			)
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("specifying a name is required for OCI images"))
		})
	})

	Context("with directory path", func() {
		var testBackendPath string

		BeforeEach(func() {
			// Create a test backend directory with required files
			testBackendPath = filepath.Join(tempDir, "source-backend")
			err := os.MkdirAll(testBackendPath, 0750)
			Expect(err).NotTo(HaveOccurred())

			// Create run.sh
			err = os.WriteFile(filepath.Join(testBackendPath, "run.sh"), []byte("#!/bin/bash\necho test"), 0755)
			Expect(err).NotTo(HaveOccurred())
		})

		It("should infer name from directory path when name is not provided", func() {
			// This test verifies that the function attempts to install using the directory name
			// The actual installation may fail due to test environment limitations
			err := services.InstallExternalBackend(
				context.Background(),
				galleries,
				systemState,
				ml,
				nil,
				testBackendPath,
				"", // name should be inferred as "source-backend"
				"",
			)
			// The function should at least attempt to install with the inferred name
			// Even if it fails for other reasons, it shouldn't fail due to missing name
			if err != nil {
				Expect(err.Error()).NotTo(ContainSubstring("name is required"))
			}
		})

		It("should use provided name when specified", func() {
			err := services.InstallExternalBackend(
				context.Background(),
				galleries,
				systemState,
				ml,
				nil,
				testBackendPath,
				"custom-backend-name",
				"",
			)
			// The function should use the provided name
			if err != nil {
				Expect(err.Error()).NotTo(ContainSubstring("name is required"))
			}
		})

		It("should support alias when provided", func() {
			err := services.InstallExternalBackend(
				context.Background(),
				galleries,
				systemState,
				ml,
				nil,
				testBackendPath,
				"custom-backend-name",
				"custom-alias",
			)
			// The function should accept alias for directory paths
			if err != nil {
				Expect(err.Error()).NotTo(ContainSubstring("alias is not supported"))
			}
		})
	})
})

var _ = Describe("GalleryOp with External Backend", func() {
	It("should have external backend fields in GalleryOp", func() {
		// Test that the GalleryOp struct has the new external backend fields
		op := services.GalleryOp[string, string]{
			ExternalURI:   "oci://example.com/backend:latest",
			ExternalName:  "test-backend",
			ExternalAlias: "test-alias",
		}

		Expect(op.ExternalURI).To(Equal("oci://example.com/backend:latest"))
		Expect(op.ExternalName).To(Equal("test-backend"))
		Expect(op.ExternalAlias).To(Equal("test-alias"))
	})
})
