package db

import (
	"path/filepath"
	"testing"
)

func TestGetPendingImagesDescFlagsGaps(t *testing.T) {
	dir := t.TempDir()
	dbPath := filepath.Join(dir, "phase1.sqlite")
	conn, err := Open(dbPath)
	if err != nil {
		t.Fatal(err)
	}
	if err := EnsureSchema(conn); err != nil {
		t.Fatal(err)
	}

	stmts := []string{
		// 1: nothing at all -> images + description gaps
		`INSERT INTO posts (keyword, slug) VALUES ('k1','k1')`,
		// 2: images done, no snippet -> description gap only
		`INSERT INTO posts (keyword, slug, images) VALUES ('k2','k2','[{"title":"t","image_url":"u"}]')`,
		// 3: images done, empty description -> description gap only
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k3','k3','[{"title":"t","image_url":"u"}]','{"related_kw":["x"],"description":[]}')`,
		// 4: fully complete -> not pending at all
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k4','k4','[{"title":"t","image_url":"u"}]','{"related_kw":["x"],"description":["a"]}')`,
		// 5: complete snippet but no images -> images gap only
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k5','k5','[]','{"related_kw":["x"],"description":["a"]}')`,
		// 6: invalid snippet JSON -> description gap
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k6','k6','[{"title":"t","image_url":"u"}]','not json')`,
		// 7: only related_kw missing -> phase 2 work, not phase 1
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k7','k7','[{"title":"t","image_url":"u"}]','{"related_kw":[],"description":["a"]}')`,
	}
	for _, s := range stmts {
		if _, err := conn.Exec(s); err != nil {
			t.Fatal(err)
		}
	}

	rows, err := GetPendingImagesDesc(conn)
	if err != nil {
		t.Fatal(err)
	}
	got := map[string]WorkRow{}
	for _, r := range rows {
		got[r.Keyword] = r
	}

	want := map[string]WorkRow{
		"k1": {NeedImages: true, NeedDesc: true},
		"k2": {NeedImages: false, NeedDesc: true},
		"k3": {NeedImages: false, NeedDesc: true},
		"k5": {NeedImages: true, NeedDesc: false},
		"k6": {NeedImages: false, NeedDesc: true},
	}
	for kw, w := range want {
		r, ok := got[kw]
		if !ok {
			t.Errorf("keyword %s should be pending in phase 1", kw)
			continue
		}
		if r.NeedImages != w.NeedImages || r.NeedDesc != w.NeedDesc {
			t.Errorf("%s flags = (%v,%v), want (%v,%v)", kw,
				r.NeedImages, r.NeedDesc, w.NeedImages, w.NeedDesc)
		}
	}
	for _, absent := range []string{"k4", "k7"} {
		if _, ok := got[absent]; ok {
			t.Errorf("%s must not be pending in phase 1", absent)
		}
	}
	if r := got["k3"]; r.Existing != `{"related_kw":["x"],"description":[]}` {
		t.Errorf("existing snippet must be preserved for merge, got %q", r.Existing)
	}
	conn.Close()
}

func TestGetPendingRelatedFlagsGaps(t *testing.T) {
	dir := t.TempDir()
	dbPath := filepath.Join(dir, "phase2.sqlite")
	conn, err := Open(dbPath)
	if err != nil {
		t.Fatal(err)
	}
	if err := EnsureSchema(conn); err != nil {
		t.Fatal(err)
	}

	stmts := []string{
		// 1: images done, no snippet -> pending
		`INSERT INTO posts (keyword, slug, images) VALUES ('k1','k1','[{"title":"t","image_url":"u"}]')`,
		// 2: empty related_kw -> pending
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k2','k2','[{"title":"t","image_url":"u"}]','{"related_kw":[],"description":["a","b"]}')`,
		// 3: related_kw present, description empty -> NOT pending (phase 1 work)
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k3','k3','[{"title":"t","image_url":"u"}]','{"related_kw":["x"],"description":[]}')`,
		// 4: complete snippet -> NOT pending
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k4','k4','[{"title":"t","image_url":"u"}]','{"related_kw":["x"],"description":["a"]}')`,
		// 5: no images -> NOT pending (rows without images are not exported)
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k5','k5','[]','{"related_kw":[],"description":[]}')`,
		// 6: invalid snippet JSON -> pending
		`INSERT INTO posts (keyword, slug, images, snippet) VALUES ('k6','k6','[{"title":"t","image_url":"u"}]','not json')`,
	}
	for _, s := range stmts {
		if _, err := conn.Exec(s); err != nil {
			t.Fatal(err)
		}
	}

	rows, err := GetPendingRelated(conn)
	if err != nil {
		t.Fatal(err)
	}
	got := map[string]string{}
	for _, r := range rows {
		got[r.Keyword] = r.Existing
	}

	for _, want := range []string{"k1", "k2", "k6"} {
		if _, ok := got[want]; !ok {
			t.Errorf("keyword %s should be pending in phase 2", want)
		}
	}
	for _, absent := range []string{"k3", "k4", "k5"} {
		if _, ok := got[absent]; ok {
			t.Errorf("%s must not be pending in phase 2", absent)
		}
	}
	if got["k2"] != `{"related_kw":[],"description":["a","b"]}` {
		t.Errorf("existing snippet must be preserved for merge, got %q", got["k2"])
	}
	conn.Close()
}

func TestSnippetGaps(t *testing.T) {
	cases := []struct {
		snippet     string
		wantDesc    bool
		wantRelated bool
	}{
		{"", true, true},
		{"[]", true, true},
		{"not json", true, true},
		{`{"related_kw":[],"description":["a"]}`, false, true},
		{`{"related_kw":["x"],"description":[]}`, true, false},
		{`{"related_kw":[],"description":[]}`, true, true},
		{`{"related_kw":["x"],"description":["a"]}`, false, false},
	}
	for _, c := range cases {
		needDesc, needRelated := snippetGaps(c.snippet)
		if needDesc != c.wantDesc || needRelated != c.wantRelated {
			t.Errorf("snippetGaps(%q) = (%v,%v), want (%v,%v)",
				c.snippet, needDesc, needRelated, c.wantDesc, c.wantRelated)
		}
	}
}

func TestCountProgress(t *testing.T) {
	dir := t.TempDir()
	dbPath := filepath.Join(dir, "progress.sqlite")
	conn, err := Open(dbPath)
	if err != nil {
		t.Fatal(err)
	}
	if err := EnsureSchema(conn); err != nil {
		t.Fatal(err)
	}

	stmts := []string{
		// complete row: everything filled
		`INSERT INTO posts (keyword, slug, images, snippet, ai_title, ai_content)
		 VALUES ('k1','k1','[{"title":"t","image_url":"u"}]','{"related_kw":["x"],"description":["a"]}','T1','C1')`,
		// images + description only
		`INSERT INTO posts (keyword, slug, images, snippet)
		 VALUES ('k2','k2','[{"title":"t","image_url":"u"}]','{"related_kw":[],"description":["b"]}')`,
		// nothing filled
		`INSERT INTO posts (keyword, slug) VALUES ('k3','k3')`,
	}
	for _, s := range stmts {
		if _, err := conn.Exec(s); err != nil {
			t.Fatal(err)
		}
	}

	p, err := CountProgress(conn)
	if err != nil {
		t.Fatal(err)
	}
	want := Progress{Total: 3, Images: 2, Descriptions: 2, Related: 1, AITitles: 1, AIArticles: 1}
	if p != want {
		t.Errorf("CountProgress = %+v, want %+v", p, want)
	}
	conn.Close()
}
