package handlers

import (
	"encoding/json"
	"fmt"
	"regexp"
)

// validManifestID matches lowercase alphanumeric slugs with optional hyphens.
// Duplicated from packages.go for use in standalone validation.
var validManifestID = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$`)

// ManifestInfo holds the parsed and validated fields from a manifest.json.
type ManifestInfo struct {
	ID            string
	Title         string
	Type          string // surface, extension, full, workflow, library
	Version       string
	Description   string
	Author        string
	Tier          string
	SchemaVersion int
	HasRoute      bool
	HasTools      bool
	HasPipes      bool
	HasHooks      bool
	HasSettings   bool
	HasExports    bool
	Dependencies  map[string]any
	Requires      []string
	Signature     string // reserved for future package signing
}

// ValidateManifest parses a manifest map and validates all required fields,
// type constraints, and structural rules. Returns a ManifestInfo or an error
// describing the first validation failure.
func ValidateManifest(manifest map[string]any) (*ManifestInfo, error) {
	if manifest == nil {
		return nil, fmt.Errorf("manifest is nil")
	}

	info := &ManifestInfo{}

	// ── Required fields ──────────────────────────────────────────
	info.ID, _ = manifest["id"].(string)
	info.Title, _ = manifest["title"].(string)
	if info.ID == "" || info.Title == "" {
		return nil, fmt.Errorf("manifest must have 'id' and 'title'")
	}

	// ── Package ID slug validation ───────────────────────────────
	if !validManifestID.MatchString(info.ID) {
		return nil, fmt.Errorf("package id must be a lowercase slug (a-z, 0-9, hyphens, 2-64 chars)")
	}

	// ── Type validation ──────────────────────────────────────────
	info.Type, _ = manifest["type"].(string)
	if info.Type == "" {
		info.Type = "surface" // backward compat
	}
	validTypes := map[string]bool{
		"surface": true, "extension": true, "full": true,
		"workflow": true, "library": true,
	}
	if !validTypes[info.Type] {
		return nil, fmt.Errorf("manifest type must be 'surface', 'extension', 'full', 'workflow', or 'library'")
	}

	// ── Extract optional fields ──────────────────────────────────
	info.Version, _ = manifest["version"].(string)
	if info.Version == "" {
		info.Version = "0.0.0"
	}
	info.Description, _ = manifest["description"].(string)
	info.Author, _ = manifest["author"].(string)
	info.Tier, _ = manifest["tier"].(string)
	if info.Tier == "" {
		info.Tier = "browser"
	}
	info.Signature, _ = manifest["signature"].(string)

	info.HasRoute = manifest["route"] != nil || manifest["routes"] != nil
	info.HasTools = manifest["tools"] != nil
	info.HasPipes = manifest["pipes"] != nil
	info.HasHooks = manifest["hooks"] != nil
	info.HasSettings = manifest["settings"] != nil
	info.HasExports = false

	if deps, ok := manifest["dependencies"].(map[string]any); ok {
		info.Dependencies = deps
	}
	if reqs, ok := manifest["requires"].([]any); ok {
		for _, r := range reqs {
			if s, ok := r.(string); ok && s != "" {
				info.Requires = append(info.Requires, s)
			}
		}
	}

	info.SchemaVersion = ParseSchemaVersion(manifest)

	// ── Type-specific constraints ────────────────────────────────
	hasExtBehavior := info.HasTools || info.HasPipes || info.HasHooks

	switch info.Type {
	case "surface":
		// route is optional for surfaces
	case "extension":
		if !hasExtBehavior {
			return nil, fmt.Errorf("extension packages require at least one of: tools, pipes, hooks")
		}
		if info.HasRoute {
			return nil, fmt.Errorf("extension packages cannot have a route — use type 'full' for both")
		}
	case "full":
		if !info.HasRoute {
			return nil, fmt.Errorf("full packages require a route")
		}
		if !hasExtBehavior && !info.HasSettings {
			return nil, fmt.Errorf("full packages require at least one of: tools, pipes, hooks, settings")
		}
	case "workflow":
		if manifest["workflow_definition"] == nil {
			return nil, fmt.Errorf("workflow packages require a 'workflow_definition' in the manifest")
		}
	case "library":
		exports, _ := manifest["exports"].([]any)
		if len(exports) == 0 {
			return nil, fmt.Errorf("library packages require an 'exports' array")
		}
		info.HasExports = true
		if info.HasTools || info.HasPipes {
			return nil, fmt.Errorf("library packages cannot have tools or pipes")
		}
		if info.HasRoute {
			return nil, fmt.Errorf("library packages cannot have a route")
		}
	}

	return info, nil
}

// ValidateManifestJSON parses raw JSON bytes into a manifest map and validates.
func ValidateManifestJSON(data []byte) (map[string]any, *ManifestInfo, error) {
	var manifest map[string]any
	if err := json.Unmarshal(data, &manifest); err != nil {
		return nil, nil, fmt.Errorf("invalid manifest.json: %w", err)
	}
	info, err := ValidateManifest(manifest)
	if err != nil {
		return nil, nil, err
	}
	return manifest, info, nil
}
