package provision

import (
	"context"
	"errors"
	"reflect"
	"strings"
	"testing"

	"sub2api-cn-relay-manager/internal/host/sub2api"
	"sub2api-cn-relay-manager/internal/store/sqlite"
)

func TestRollbackServiceDeletesManagedResourcesInDependencyOrder(t *testing.T) {
	host := &fakeHostAdapter{
		managedSnapshot: sub2api.ManagedResourceSnapshot{
			Groups:   []sub2api.NamedResource{{ID: "group_1", Name: "DeepSeek 默认分组"}},
			Channels: []sub2api.NamedResource{{ID: "channel_1", Name: "DeepSeek 默认渠道"}},
			Plans:    []sub2api.NamedResource{{ID: "plan_1", Name: "DeepSeek 默认套餐"}},
			Accounts: []sub2api.NamedResource{{ID: "account_1", Name: "deepseek-01"}, {ID: "account_2", Name: "deepseek-02"}},
		},
	}

	svc := NewRollbackService(host)
	report, err := svc.Rollback(context.Background(), RollbackRequest{Provider: sampleProviderManifest()})
	if err != nil {
		t.Fatalf("Rollback() error = %v", err)
	}

	if report.AccountsDeleted != 2 || report.PlansDeleted != 1 || report.ChannelsDeleted != 1 || report.GroupsDeleted != 1 {
		t.Fatalf("Rollback() report = %+v, want all managed resources deleted", report)
	}
	want := []string{"account:account_2", "account:account_1", "plan:plan_1", "channel:channel_1", "group:group_1"}
	if !reflect.DeepEqual(host.deletedResources, want) {
		t.Fatalf("deleted resources = %#v, want %#v", host.deletedResources, want)
	}
}

func TestRollbackServiceReturnsEmptyReportWhenNoManagedResourcesExist(t *testing.T) {
	host := &fakeHostAdapter{}
	svc := NewRollbackService(host)

	report, err := svc.Rollback(context.Background(), RollbackRequest{Provider: sampleProviderManifest()})
	if err != nil {
		t.Fatalf("Rollback() error = %v", err)
	}
	if report.AccountsDeleted != 0 || report.PlansDeleted != 0 || report.ChannelsDeleted != 0 || report.GroupsDeleted != 0 {
		t.Fatalf("Rollback() report = %+v, want zero deletions", report)
	}
	if len(host.deletedResources) != 0 {
		t.Fatalf("deleted resources = %#v, want none", host.deletedResources)
	}
}

func TestRollbackServiceRollbackStoredResourcesDeletesOnlyProvidedIDs(t *testing.T) {
	host := &fakeHostAdapter{}
	svc := NewRollbackService(host)

	report, err := svc.RollbackStoredResources(context.Background(), []sqlite.ManagedResource{
		{BatchID: 2, ResourceType: "group", HostResourceID: "group_shared", ResourceName: "DeepSeek 默认分组"},
		{BatchID: 2, ResourceType: "account", HostResourceID: "account_2", ResourceName: "deepseek-02"},
	})
	if err != nil {
		t.Fatalf("RollbackStoredResources() error = %v", err)
	}
	if report.AccountsDeleted != 1 || report.GroupsDeleted != 1 || report.ChannelsDeleted != 0 || report.PlansDeleted != 0 {
		t.Fatalf("RollbackStoredResources() report = %+v", report)
	}
	want := []string{"account:account_2", "group:group_shared"}
	if !reflect.DeepEqual(host.deletedResources, want) {
		t.Fatalf("deleted resources = %#v, want %#v", host.deletedResources, want)
	}
}

func TestRollbackServiceRequiresHost(t *testing.T) {
	svc := NewRollbackService(nil)

	if _, err := svc.Rollback(context.Background(), RollbackRequest{Provider: sampleProviderManifest()}); err == nil || err.Error() != "rollback host is required" {
		t.Fatalf("Rollback() error = %v, want rollback host is required", err)
	}
	if _, err := svc.RollbackStoredResources(context.Background(), nil); err == nil || err.Error() != "rollback host is required" {
		t.Fatalf("RollbackStoredResources() error = %v, want rollback host is required", err)
	}
}

func TestRollbackServiceCollectsDeleteErrors(t *testing.T) {
	host := &fakeHostAdapter{
		managedSnapshot: sub2api.ManagedResourceSnapshot{
			Groups:   []sub2api.NamedResource{{ID: "group_1", Name: "DeepSeek 默认分组"}},
			Channels: []sub2api.NamedResource{{ID: "channel_1", Name: "DeepSeek 默认渠道"}},
			Plans:    []sub2api.NamedResource{{ID: "plan_1", Name: "DeepSeek 默认套餐"}},
			Accounts: []sub2api.NamedResource{{ID: "account_1", Name: "deepseek-01"}, {ID: "account_2", Name: "deepseek-02"}},
		},
		deleteErrors: map[string]error{
			"account:account_2": errors.New("account blocked"),
			"channel:channel_1": errors.New("channel blocked"),
		},
	}

	report, err := NewRollbackService(host).Rollback(context.Background(), RollbackRequest{Provider: sampleProviderManifest()})
	if err == nil {
		t.Fatal("Rollback() error = nil, want joined delete errors")
	}
	if report.AccountsDeleted != 1 || report.PlansDeleted != 1 || report.ChannelsDeleted != 0 || report.GroupsDeleted != 1 {
		t.Fatalf("Rollback() report = %+v, want partial success counts", report)
	}
	if !strings.Contains(err.Error(), "delete account account_2") || !strings.Contains(err.Error(), "delete channel channel_1") {
		t.Fatalf("Rollback() error = %v, want joined account/channel errors", err)
	}
}

func TestNamedResourceSnapshotFromStoredIgnoresUnknownTypes(t *testing.T) {
	snapshot := namedResourceSnapshotFromStored([]sqlite.ManagedResource{
		{ResourceType: "group", HostResourceID: "group_1", ResourceName: "g1"},
		{ResourceType: "mystery", HostResourceID: "mystery_1", ResourceName: "m1"},
		{ResourceType: "account", HostResourceID: "account_1", ResourceName: "a1"},
	})

	if len(snapshot.Groups) != 1 || snapshot.Groups[0].ID != "group_1" {
		t.Fatalf("snapshot.Groups = %#v, want group_1 only", snapshot.Groups)
	}
	if len(snapshot.Accounts) != 1 || snapshot.Accounts[0].ID != "account_1" {
		t.Fatalf("snapshot.Accounts = %#v, want account_1 only", snapshot.Accounts)
	}
	if len(snapshot.Plans) != 0 || len(snapshot.Channels) != 0 {
		t.Fatalf("snapshot = %#v, want unknown type ignored", snapshot)
	}
}
