package main

import (
	"fmt"
	"sync"
)

type Subject struct {
	ID, Tenant, Role string
	Active           bool
	Groups           map[string]bool
}
type Document struct {
	ID, Tenant, Owner, ViewerGroup string
	Locked                         bool
}

// Subject and Document must come from trusted server-side records.
func Permit(s Subject, action string, d Document) bool {
	if !s.Active || s.ID == "" || s.Tenant == "" || d.ID == "" || s.Tenant != d.Tenant {
		return false
	}
	switch action {
	case "read":
		return s.Role == "admin" || s.ID == d.Owner || (d.ViewerGroup != "" && s.Groups[d.ViewerGroup])
	case "edit":
		return !d.Locked && (s.Role == "admin" || s.ID == d.Owner)
	case "delete":
		return s.Role == "admin"
	default:
		return false
	}
}

// The lock makes the local authorization check and modification one operation.
// In a real database use a suitable transaction or a conditional UPDATE.
type Store struct {
	mu        sync.Mutex
	documents map[string]Document
}

func (st *Store) ChangeOwner(s Subject, id, next string) bool {
	st.mu.Lock()
	defer st.mu.Unlock()
	d, ok := st.documents[id]
	if !ok || next == "" || !Permit(s, "edit", d) {
		return false
	}
	d.Owner = next
	st.documents[id] = d
	return true
}
func fixture() (Subject, Document) {
	return Subject{"alice", "team-a", "member", true, map[string]bool{"design": true}}, Document{"doc-1", "team-a", "alice", "", false}
}
func main() {
	alice, d := fixture()
	fmt.Println("owner-read:", Permit(alice, "read", d))
	other := d
	other.Owner = "bob"
	fmt.Println("other-user:", Permit(alice, "read", other))
	shared := other
	shared.ViewerGroup = "design"
	fmt.Println("group-share:", Permit(alice, "read", shared))
	cross := d
	cross.Tenant = "team-b"
	fmt.Println("cross-tenant:", Permit(alice, "read", cross))
	fmt.Println("member-delete:", Permit(alice, "delete", d))
	fmt.Println("unknown-action:", Permit(alice, "publish", d))
	admin := alice
	admin.Role = "admin"
	fmt.Println("admin-other-tenant:", Permit(admin, "delete", cross))
	st := Store{documents: map[string]Document{d.ID: d}}
	fmt.Println("owner-change:", st.ChangeOwner(alice, d.ID, "bob"))
	fmt.Println("old-owner-write:", st.ChangeOwner(alice, d.ID, "alice"))
}
