package packages

import (
	"encoding/json"
	"net/http"
	"reflect"
	"strings"
	"testing"

	"nadir/internal/oscmd"

	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humago"
	"github.com/danielgtaylor/huma/v2/humatest"
)

func TestPackagesHandlers(t *testing.T) {
	managers := []string{"dnf", "apt", "pacman"}

	for _, mgrName := range managers {
		t.Run(mgrName, func(t *testing.T) {
			mux := http.NewServeMux()
			api := humatest.Wrap(t, humago.New(mux, huma.DefaultConfig("Test", "1.0.0")))

			m := &Module{pm: manager{name: mgrName}}
			m.Register(api)

			oscmd.SetMock("rpm", func(args []string) oscmd.MockCommand {
				return oscmd.MockCommand{Stdout: "htop\t3.3.0-1\n", ExitCode: 0}
			})
			oscmd.SetMock("dpkg-query", func(args []string) oscmd.MockCommand {
				return oscmd.MockCommand{Stdout: "htop\t3.3.0-1\n", ExitCode: 0}
			})
			oscmd.SetMock("pacman", func(args []string) oscmd.MockCommand {
				if reflect.DeepEqual(args, []string{"-Q"}) {
					return oscmd.MockCommand{Stdout: "htop 3.3.0-1\n", ExitCode: 0}
				}
				if reflect.DeepEqual(args, []string{"-Qu"}) {
					return oscmd.MockCommand{Stdout: "linux 6.9.1-1 -> 6.9.2-1\n", ExitCode: 0}
				}
				return oscmd.MockCommand{Lines: []string{"pacman output"}, ExitCode: 0}
			})
			oscmd.SetMock("dnf", func(args []string) oscmd.MockCommand {
				if reflect.DeepEqual(args, []string{"-q", "check-update"}) {
					return oscmd.MockCommand{Stdout: "Upgrades\ncode.x86_64 1.125.1-1 code\n", ExitCode: 100}
				}
				return oscmd.MockCommand{Lines: []string{"dnf output"}, ExitCode: 0}
			})
			oscmd.SetMock("apt", func(args []string) oscmd.MockCommand {
				return oscmd.MockCommand{Stdout: "Listing...\nvim/jammy 2:8.2.3995 amd64 [upgradable]\n", ExitCode: 0}
			})
			oscmd.SetMock("apt-get", func(args []string) oscmd.MockCommand {
				return oscmd.MockCommand{Lines: []string{"apt-get output"}, ExitCode: 0}
			})
			defer oscmd.ClearMocks()

			// Test list installed
			resp := api.Get("/api/packages")
			if resp.Code != http.StatusOK {
				t.Errorf("list installed: got %d, want %d", resp.Code, http.StatusOK)
			}
			var listRes ListOutput
			if err := json.Unmarshal(resp.Body.Bytes(), &listRes.Body); err != nil {
				t.Fatal(err)
			}
			if len(listRes.Body.Packages) != 1 || listRes.Body.Packages[0].Name != "htop" {
				t.Errorf("unexpected installed packages: %+v", listRes.Body)
			}

			// Test list updates
			resp = api.Get("/api/packages/updates")
			if resp.Code != http.StatusOK {
				t.Errorf("list updates: got %d, want %d", resp.Code, http.StatusOK)
			}
			if err := json.Unmarshal(resp.Body.Bytes(), &listRes.Body); err != nil {
				t.Fatal(err)
			}
			if len(listRes.Body.Packages) != 1 {
				t.Errorf("unexpected updates: %+v", listRes.Body)
			}

			// Test install (SSE)
			resp = api.Post("/api/packages", struct {
				Name string `json:"name"`
			}{
				Name: "htop",
			})
			if resp.Code != http.StatusOK {
				t.Errorf("install: got %d, want %d", resp.Code, http.StatusOK)
			}
			bodyStr := resp.Body.String()
			if !strings.Contains(bodyStr, "done") {
				t.Errorf("install stream output missing done: %q", bodyStr)
			}

			// Test remove (SSE)
			resp = api.Delete("/api/packages/htop")
			if resp.Code != http.StatusOK {
				t.Errorf("remove: got %d, want %d", resp.Code, http.StatusOK)
			}
			bodyStr = resp.Body.String()
			if !strings.Contains(bodyStr, "done") {
				t.Errorf("remove stream output missing done: %q", bodyStr)
			}

			// Test upgrade (SSE)
			resp = api.Post("/api/packages/upgrade", struct{}{})
			if resp.Code != http.StatusOK {
				t.Errorf("upgrade: got %d, want %d", resp.Code, http.StatusOK)
			}
			bodyStr = resp.Body.String()
			if !strings.Contains(bodyStr, "done") {
				t.Errorf("upgrade stream output missing done: %q", bodyStr)
			}
		})
	}
}
