package zddc

import (
	"os"
	"path/filepath"
	"testing"
)

// TestParseFile_TablesRoundTrip exercises the Tables field added to
// support the table tool. A .zddc with a tables: map should round-trip
// through ParseFile cleanly without disturbing existing fields.
func TestParseFile_TablesRoundTrip(t *testing.T) {
	root := t.TempDir()
	body := `acl:
  permissions:
    "*@example.com": rwcd
title: Demo
apps:
  archive: stable
tables:
  MDL: ./MDL.table.yaml
  Subcontracts: ./contracts/subs.table.yaml
roles:
  reviewers:
    members: ["bob@example.com"]
`
	path := filepath.Join(root, ".zddc")
	if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
		t.Fatalf("write .zddc: %v", err)
	}
	zf, err := ParseFile(path)
	if err != nil {
		t.Fatalf("ParseFile: %v", err)
	}
	if got := zf.Tables["MDL"]; got != "./MDL.table.yaml" {
		t.Errorf("Tables[MDL] = %q want %q", got, "./MDL.table.yaml")
	}
	if got := zf.Tables["Subcontracts"]; got != "./contracts/subs.table.yaml" {
		t.Errorf("Tables[Subcontracts] = %q want %q", got, "./contracts/subs.table.yaml")
	}
	// Sibling fields should still parse.
	if zf.Title != "Demo" {
		t.Errorf("Title = %q want %q", zf.Title, "Demo")
	}
	if got := zf.Apps["archive"]; got != "stable" {
		t.Errorf("Apps[archive] = %q want %q", got, "stable")
	}
	if r, ok := zf.Roles["reviewers"]; !ok || len(r.Members) != 1 {
		t.Errorf("Roles[reviewers] = %+v want one member", r)
	}
	if got := zf.ACL.Permissions["*@example.com"]; got != "rwcd" {
		t.Errorf("ACL.Permissions[*@example.com] = %q want rwcd", got)
	}
}

// TestParseFile_TablesEmptyOmitted confirms that a .zddc without a
// tables: key parses with a nil Tables map (omitempty round-trip).
func TestParseFile_TablesEmptyOmitted(t *testing.T) {
	root := t.TempDir()
	body := `title: NoTables
acl:
  permissions:
    "*@example.com": r
`
	path := filepath.Join(root, ".zddc")
	if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
		t.Fatalf("write .zddc: %v", err)
	}
	zf, err := ParseFile(path)
	if err != nil {
		t.Fatalf("ParseFile: %v", err)
	}
	if zf.Tables != nil {
		t.Errorf("Tables = %+v want nil for absent tables: key", zf.Tables)
	}
}

// Inherit defaults to "inherit normally" when the field is absent;
// explicit true behaves the same; explicit false marks the level as
// a fence.
func TestParseFile_InheritDirective(t *testing.T) {
	cases := []struct {
		name        string
		body        string
		wantPtrNil  bool
		wantInherit bool
	}{
		{
			name: "absent → nil pointer, inherits",
			body: `acl:
  permissions:
    "*@example.com": r
`,
			wantPtrNil:  true,
			wantInherit: true,
		},
		{
			name: "explicit true → non-nil, inherits",
			body: `acl:
  inherit: true
  permissions:
    "*@example.com": r
`,
			wantPtrNil:  false,
			wantInherit: true,
		},
		{
			name: "explicit false → non-nil, fences",
			body: `acl:
  inherit: false
  permissions:
    "*@vendor.com": rwcd
`,
			wantPtrNil:  false,
			wantInherit: false,
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			path := filepath.Join(t.TempDir(), ".zddc")
			if err := os.WriteFile(path, []byte(tc.body), 0o644); err != nil {
				t.Fatalf("write: %v", err)
			}
			zf, err := ParseFile(path)
			if err != nil {
				t.Fatalf("ParseFile: %v", err)
			}
			if (zf.ACL.Inherit == nil) != tc.wantPtrNil {
				t.Errorf("Inherit pointer nil=%v want %v", zf.ACL.Inherit == nil, tc.wantPtrNil)
			}
			if got := zf.ACL.InheritsAncestors(); got != tc.wantInherit {
				t.Errorf("InheritsAncestors() = %v want %v", got, tc.wantInherit)
			}
		})
	}
}
