package schemacatalog

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
)

const PermissionStateDirectWriterInventorySchema = "nexuscrm.permission-state-direct-writer-inventory.v1"

var permissionStateDirectWriterSymbolPattern = regexp.MustCompile(
	`^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$`,
)

// PermissionStateDirectWriter identifies one production Go function that
// directly mutates at least one managed Permission State surface.
//
// Registration is a census, not an enforced-readiness claim. Writer closure
// separately proves that each listed function participates through the shared
// Authority or is rejected before mutation while enforced.
type PermissionStateDirectWriter struct {
	WriterID   string   `json:"writerId"`
	SurfaceIDs []string `json:"surfaceIds"`
}

// PermissionStateDirectWriterInventory is the immutable generated projection
// of production direct SQL mutation sites joined to managed semantic surfaces.
type PermissionStateDirectWriterInventory struct {
	writers   []PermissionStateDirectWriter
	canonical []byte
	digest    string
}

type permissionStateDirectWriterDeclaration struct {
	WriterID   string
	SurfaceIDs []string
}

type canonicalPermissionStateDirectWriterInventory struct {
	SchemaVersion string                        `json:"schemaVersion"`
	Writers       []PermissionStateDirectWriter `json:"writers"`
}

// ManagedPermissionStateDirectWriterInventory returns the executable direct
// writer census generated from the platform-owned catalog and production Go
// mutation sites.
func ManagedPermissionStateDirectWriterInventory() (PermissionStateDirectWriterInventory, error) {
	surfaces, err := ManagedPermissionStateMutationInventory()
	if err != nil {
		return PermissionStateDirectWriterInventory{}, err
	}
	return permissionStateDirectWriterInventoryFromDeclarations(
		surfaces,
		managedPermissionStateDirectWriterDeclarations,
	)
}

// SchemaVersion returns the canonical direct-writer inventory schema.
func (inventory PermissionStateDirectWriterInventory) SchemaVersion() string {
	return PermissionStateDirectWriterInventorySchema
}

// Writers returns a deep copy in canonical WriterID order.
func (inventory PermissionStateDirectWriterInventory) Writers() []PermissionStateDirectWriter {
	return clonePermissionStateDirectWriters(inventory.writers)
}

// Writer returns a deep copy of one generated writer registration.
func (inventory PermissionStateDirectWriterInventory) Writer(
	writerID string,
) (PermissionStateDirectWriter, bool) {
	for _, writer := range inventory.writers {
		if writer.WriterID == writerID {
			return clonePermissionStateDirectWriter(writer), true
		}
	}
	return PermissionStateDirectWriter{}, false
}

// CanonicalJSON returns an immutable copy of the digest input.
func (inventory PermissionStateDirectWriterInventory) CanonicalJSON() []byte {
	return append([]byte(nil), inventory.canonical...)
}

// Digest returns the sha256-qualified digest of CanonicalJSON.
func (inventory PermissionStateDirectWriterInventory) Digest() string {
	return inventory.digest
}

func permissionStateDirectWriterInventoryFromDeclarations(
	surfaces PermissionStateMutationInventory,
	declarations []permissionStateDirectWriterDeclaration,
) (PermissionStateDirectWriterInventory, error) {
	managedSurfaceIDs := make(map[string]struct{})
	for _, surface := range surfaces.Surfaces() {
		managedSurfaceIDs[surface.SurfaceID] = struct{}{}
	}
	if len(managedSurfaceIDs) == 0 {
		return PermissionStateDirectWriterInventory{}, errors.New(
			"permission state direct writer inventory: managed surfaces are empty",
		)
	}

	writers := make([]PermissionStateDirectWriter, 0, len(declarations))
	writerIDs := make(map[string]struct{}, len(declarations))
	for _, declaration := range declarations {
		writerID := strings.TrimSpace(declaration.WriterID)
		sourcePath, symbol, hasSymbol := strings.Cut(writerID, "#")
		cleanPath := filepath.ToSlash(filepath.Clean(sourcePath))
		if writerID == "" || writerID != declaration.WriterID ||
			!hasSymbol || symbol == "" ||
			!permissionStateDirectWriterSymbolPattern.MatchString(symbol) ||
			strings.ContainsAny(writerID, " \t\r\n") ||
			sourcePath != cleanPath ||
			filepath.IsAbs(sourcePath) ||
			sourcePath == ".." ||
			strings.HasPrefix(sourcePath, "../") ||
			filepath.Ext(sourcePath) != ".go" {
			return PermissionStateDirectWriterInventory{}, fmt.Errorf(
				"permission state direct writer inventory: invalid writer ID %q",
				declaration.WriterID,
			)
		}
		if _, duplicate := writerIDs[writerID]; duplicate {
			return PermissionStateDirectWriterInventory{}, fmt.Errorf(
				"permission state direct writer inventory: duplicate writer %s",
				writerID,
			)
		}
		writerIDs[writerID] = struct{}{}

		if len(declaration.SurfaceIDs) == 0 {
			return PermissionStateDirectWriterInventory{}, fmt.Errorf(
				"permission state direct writer inventory: writer %s has no managed surfaces",
				writerID,
			)
		}
		surfaceIDs := make([]string, 0, len(declaration.SurfaceIDs))
		seenSurfaces := make(map[string]struct{}, len(declaration.SurfaceIDs))
		for _, declaredSurfaceID := range declaration.SurfaceIDs {
			surfaceID := strings.TrimSpace(declaredSurfaceID)
			if surfaceID == "" || surfaceID != declaredSurfaceID {
				return PermissionStateDirectWriterInventory{}, fmt.Errorf(
					"permission state direct writer inventory: writer %s has invalid surface ID %q",
					writerID,
					declaredSurfaceID,
				)
			}
			if _, ok := managedSurfaceIDs[surfaceID]; !ok {
				return PermissionStateDirectWriterInventory{}, fmt.Errorf(
					"permission state direct writer inventory: writer %s references unknown surface %s",
					writerID,
					surfaceID,
				)
			}
			if _, duplicate := seenSurfaces[surfaceID]; duplicate {
				return PermissionStateDirectWriterInventory{}, fmt.Errorf(
					"permission state direct writer inventory: writer %s duplicates surface %s",
					writerID,
					surfaceID,
				)
			}
			seenSurfaces[surfaceID] = struct{}{}
			surfaceIDs = append(surfaceIDs, surfaceID)
		}
		sort.Strings(surfaceIDs)
		writers = append(writers, PermissionStateDirectWriter{
			WriterID:   writerID,
			SurfaceIDs: surfaceIDs,
		})
	}
	if len(writers) == 0 {
		return PermissionStateDirectWriterInventory{}, errors.New(
			"permission state direct writer inventory: declarations are empty",
		)
	}

	sort.Slice(writers, func(i, j int) bool {
		return writers[i].WriterID < writers[j].WriterID
	})
	canonical, err := json.Marshal(canonicalPermissionStateDirectWriterInventory{
		SchemaVersion: PermissionStateDirectWriterInventorySchema,
		Writers:       writers,
	})
	if err != nil {
		return PermissionStateDirectWriterInventory{}, fmt.Errorf(
			"permission state direct writer inventory: encode canonical projection: %w",
			err,
		)
	}
	sum := sha256.Sum256(canonical)
	return PermissionStateDirectWriterInventory{
		writers:   clonePermissionStateDirectWriters(writers),
		canonical: append([]byte(nil), canonical...),
		digest:    "sha256:" + hex.EncodeToString(sum[:]),
	}, nil
}

func clonePermissionStateDirectWriters(
	input []PermissionStateDirectWriter,
) []PermissionStateDirectWriter {
	output := make([]PermissionStateDirectWriter, len(input))
	for index, writer := range input {
		output[index] = clonePermissionStateDirectWriter(writer)
	}
	return output
}

func clonePermissionStateDirectWriter(
	writer PermissionStateDirectWriter,
) PermissionStateDirectWriter {
	writer.SurfaceIDs = append([]string(nil), writer.SurfaceIDs...)
	return writer
}
