package store

import (
	"errors"
	"testing"
)

func TestCreateAndGetContainer(t *testing.T) {
	s := newTestStore(t)

	c, err := s.CreateContainer(Container{
		WorkloadID: "wl-1", WorkloadKind: "project", Role: "prod",
		ContainerID: "abc123", ImageRef: "nginx:1", ImageTag: "1",
		State: "running", Port: 80, Subdomain: "prod-app",
	})
	if err != nil {
		t.Fatalf("CreateContainer: %v", err)
	}
	if c.ID == "" {
		t.Fatal("container ID should be set")
	}
	if c.Host != "local" {
		t.Fatalf("default host should be 'local', got %q", c.Host)
	}

	got, err := s.GetContainerByID(c.ID)
	if err != nil {
		t.Fatalf("GetContainerByID: %v", err)
	}
	if got.ContainerID != "abc123" || got.Subdomain != "prod-app" {
		t.Fatalf("got %+v", got)
	}
}

func TestUpsertContainerInsert(t *testing.T) {
	s := newTestStore(t)

	if err := s.UpsertContainer(Container{
		ID: "fixed-id", WorkloadID: "wl-1", WorkloadKind: "project",
		Role: "dev", State: "running",
	}); err != nil {
		t.Fatalf("UpsertContainer insert: %v", err)
	}

	got, err := s.GetContainerByID("fixed-id")
	if err != nil {
		t.Fatalf("GetContainerByID: %v", err)
	}
	if got.State != "running" {
		t.Fatalf("got state %q", got.State)
	}
}

func TestUpsertContainerUpdate(t *testing.T) {
	s := newTestStore(t)

	_ = s.UpsertContainer(Container{
		ID: "fixed-id", WorkloadID: "wl-1", WorkloadKind: "project",
		Role: "dev", State: "starting",
	})

	if err := s.UpsertContainer(Container{
		ID: "fixed-id", WorkloadID: "wl-1", WorkloadKind: "project",
		Role: "dev", State: "running", Port: 8080,
	}); err != nil {
		t.Fatalf("UpsertContainer update: %v", err)
	}

	got, _ := s.GetContainerByID("fixed-id")
	if got.State != "running" || got.Port != 8080 {
		t.Fatalf("upsert did not update fields: %+v", got)
	}
}

func TestUpsertContainerRequiresID(t *testing.T) {
	s := newTestStore(t)
	if err := s.UpsertContainer(Container{WorkloadID: "wl-1"}); err == nil {
		t.Fatal("UpsertContainer without ID should fail")
	}
}

func TestGetContainerByDockerID(t *testing.T) {
	s := newTestStore(t)

	c, _ := s.CreateContainer(Container{
		WorkloadID: "wl-1", WorkloadKind: "project", Role: "prod",
		ContainerID: "docker-xyz", State: "running",
	})

	got, err := s.GetContainerByDockerID("docker-xyz")
	if err != nil {
		t.Fatalf("GetContainerByDockerID: %v", err)
	}
	if got.ID != c.ID {
		t.Fatalf("got container %s, want %s", got.ID, c.ID)
	}

	if _, err := s.GetContainerByDockerID(""); !errors.Is(err, ErrNotFound) {
		t.Fatalf("empty docker id should be NotFound, got %v", err)
	}
	if _, err := s.GetContainerByDockerID("ghost"); !errors.Is(err, ErrNotFound) {
		t.Fatalf("unknown docker id should be NotFound, got %v", err)
	}
}

func TestListContainersByWorkload(t *testing.T) {
	s := newTestStore(t)

	s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project", Role: "a"})
	s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project", Role: "b"})
	s.CreateContainer(Container{WorkloadID: "wl-2", WorkloadKind: "project", Role: "c"})

	out, err := s.ListContainersByWorkload("wl-1")
	if err != nil {
		t.Fatalf("ListContainersByWorkload: %v", err)
	}
	if len(out) != 2 {
		t.Fatalf("expected 2 containers, got %d", len(out))
	}
}

func TestListContainersWithFilter(t *testing.T) {
	s := newTestStore(t)

	s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project", State: "running"})
	s.CreateContainer(Container{WorkloadID: "wl-2", WorkloadKind: "stack", State: "running"})
	s.CreateContainer(Container{WorkloadID: "wl-3", WorkloadKind: "site", State: "stopped"})

	out, err := s.ListContainers(ContainerFilter{WorkloadKind: "project"})
	if err != nil {
		t.Fatalf("ListContainers kind filter: %v", err)
	}
	if len(out) != 1 || out[0].WorkloadKind != "project" {
		t.Fatalf("kind filter wrong: %+v", out)
	}

	out, err = s.ListContainers(ContainerFilter{State: "running"})
	if err != nil {
		t.Fatalf("ListContainers state filter: %v", err)
	}
	if len(out) != 2 {
		t.Fatalf("expected 2 running, got %d", len(out))
	}

	out, err = s.ListContainers(ContainerFilter{})
	if err != nil {
		t.Fatalf("ListContainers no filter: %v", err)
	}
	if len(out) != 3 {
		t.Fatalf("expected 3 with no filter, got %d", len(out))
	}
}

func TestListContainersByApp(t *testing.T) {
	s := newTestStore(t)

	app, _ := s.CreateApp(App{Name: "my-saas"})
	w1, _ := s.CreateWorkload(Workload{Kind: "project", RefID: "p1", Name: "web", AppID: app.ID})
	w2, _ := s.CreateWorkload(Workload{Kind: "stack", RefID: "s1", Name: "worker", AppID: app.ID})
	wOther, _ := s.CreateWorkload(Workload{Kind: "project", RefID: "p2", Name: "other"})

	s.CreateContainer(Container{WorkloadID: w1.ID, WorkloadKind: "project"})
	s.CreateContainer(Container{WorkloadID: w2.ID, WorkloadKind: "stack"})
	s.CreateContainer(Container{WorkloadID: wOther.ID, WorkloadKind: "project"})

	out, err := s.ListContainers(ContainerFilter{AppID: app.ID})
	if err != nil {
		t.Fatalf("ListContainers AppID: %v", err)
	}
	if len(out) != 2 {
		t.Fatalf("expected 2 containers in app, got %d", len(out))
	}
}

func TestUpdateContainerState(t *testing.T) {
	s := newTestStore(t)

	c, _ := s.CreateContainer(Container{
		WorkloadID: "wl-1", WorkloadKind: "project", State: "starting",
	})

	if err := s.UpdateContainerState(c.ID, "running"); err != nil {
		t.Fatalf("UpdateContainerState: %v", err)
	}
	got, _ := s.GetContainerByID(c.ID)
	if got.State != "running" {
		t.Fatalf("got state %q", got.State)
	}
	if got.LastSeenAt == "" {
		t.Fatal("last_seen_at should have been bumped")
	}
}

func TestMarkContainerMissing(t *testing.T) {
	s := newTestStore(t)

	c, _ := s.CreateContainer(Container{
		WorkloadID: "wl-1", WorkloadKind: "project", State: "running",
	})
	if err := s.MarkContainerMissing(c.ID); err != nil {
		t.Fatalf("MarkContainerMissing: %v", err)
	}
	got, _ := s.GetContainerByID(c.ID)
	if got.State != "missing" {
		t.Fatalf("got state %q, want missing", got.State)
	}
}

func TestDeleteContainersByWorkload(t *testing.T) {
	s := newTestStore(t)

	s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project"})
	s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project"})
	s.CreateContainer(Container{WorkloadID: "wl-2", WorkloadKind: "project"})

	if err := s.DeleteContainersByWorkload("wl-1"); err != nil {
		t.Fatalf("DeleteContainersByWorkload: %v", err)
	}

	out, _ := s.ListContainersByWorkload("wl-1")
	if len(out) != 0 {
		t.Fatalf("expected 0 after delete, got %d", len(out))
	}
	out, _ = s.ListContainersByWorkload("wl-2")
	if len(out) != 1 {
		t.Fatalf("untouched workload should still have 1, got %d", len(out))
	}
}
