//
//  Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

package security

import (
	"fmt"
	"strings"
)

// Guard provides security policy enforcement
type Guard struct {
	trustedRepos map[string]bool
	policy       map[string][3]string
}

// NewGuard creates a new security guard
func NewGuard() *Guard {
	return &Guard{
		trustedRepos: TrustedRepos,
		policy:       InstallPolicy,
	}
}

// extractCanonicalRepo extracts the canonical owner/repo from an identifier
// Supports formats: "owner/repo", "github.com/owner/repo/path", "owner/repo/path"
func extractCanonicalRepo(identifier string) string {
	// Normalize the identifier
	identifier = strings.TrimSpace(identifier)
	identifier = strings.ToLower(identifier)

	// Remove protocol prefix if present
	if idx := strings.Index(identifier, "://"); idx != -1 {
		identifier = identifier[idx+3:]
	}

	// Remove github.com prefix if present
	if strings.HasPrefix(identifier, "github.com/") {
		identifier = strings.TrimPrefix(identifier, "github.com/")
	}

	// Split into parts
	parts := strings.Split(identifier, "/")
	if len(parts) < 2 {
		return ""
	}

	// Extract owner and repo (first two components)
	owner := strings.TrimSpace(parts[0])
	repo := strings.TrimSpace(parts[1])

	if owner == "" || repo == "" {
		return ""
	}

	return owner + "/" + repo
}

// ResolveTrustLevel determines the trust level based on source and identifier
func (g *Guard) ResolveTrustLevel(source, identifier string) string {
	// Official/builtin source
	if source == "official" || source == "builtin" {
		return "builtin"
	}

	// Extract canonical repo key and check against trusted repositories
	canonicalRepo := extractCanonicalRepo(identifier)
	if canonicalRepo != "" && g.trustedRepos[canonicalRepo] {
		return "trusted"
	}

	// Default to community
	return "community"
}

// ShouldAllowInstall determines if installation should be allowed based on scan results
// Returns (allowed bool, reason string)
func (g *Guard) ShouldAllowInstall(result *ScanResult, force bool) (bool, string) {
	policy, ok := g.policy[result.TrustLevel]
	if !ok {
		policy = g.policy["community"]
	}

	vi, ok := VerdictIndex[result.Verdict]
	if !ok {
		vi = 2 // dangerous
	}

	decision := policy[vi]

	switch decision {
	case "allow":
		return true, fmt.Sprintf("Allowed (%s source, %s verdict)", result.TrustLevel, result.Verdict)
	case "ask":
		return false, fmt.Sprintf("Requires confirmation (%s source + %s verdict, %d findings)",
			result.TrustLevel, result.Verdict, len(result.Findings))
	case "block":
		if force {
			return true, fmt.Sprintf("Force-installed despite %s verdict (%d findings)",
				result.Verdict, len(result.Findings))
		}
		return false, fmt.Sprintf("Blocked (%s source + %s verdict, %d findings). Use --force to override.",
			result.TrustLevel, result.Verdict, len(result.Findings))
	}

	return false, "Unknown policy decision"
}

// FormatScanReport formats a scan result for display
func (g *Guard) FormatScanReport(result *ScanResult) string {
	var sb strings.Builder

	sb.WriteString("╔════════════════════════════════════════════════════════════════╗\n")
	sb.WriteString(fmt.Sprintf("║ Security Scan Report: %-40s ║\n", result.SkillName))
	sb.WriteString("╚════════════════════════════════════════════════════════════════╝\n")
	sb.WriteString(fmt.Sprintf("Source:      %s\n", result.Source))
	sb.WriteString(fmt.Sprintf("Trust Level: %s\n", result.TrustLevel))
	sb.WriteString(fmt.Sprintf("Verdict:     %s\n", result.Verdict))
	sb.WriteString(fmt.Sprintf("Findings:    %d\n", len(result.Findings)))

	if len(result.Findings) > 0 {
		sb.WriteString("\n─── Findings ───\n")

		// Group by severity
		severityOrder := []string{"critical", "high", "medium", "low"}
		for _, sev := range severityOrder {
			for _, f := range result.Findings {
				if f.Severity == sev {
					sb.WriteString(fmt.Sprintf("\n[%s] %s\n", strings.ToUpper(sev), f.PatternID))
					sb.WriteString(fmt.Sprintf("  Category: %s\n", f.Category))
					sb.WriteString(fmt.Sprintf("  File: %s:%d\n", f.File, f.Line))
					sb.WriteString(fmt.Sprintf("  Match: %s\n", f.Match))
					sb.WriteString(fmt.Sprintf("  Description: %s\n", f.Description))
				}
			}
		}
	}

	sb.WriteString("\n")
	return sb.String()
}

// AddTrustedRepo adds a repository to the trusted list
func (g *Guard) AddTrustedRepo(repo string) {
	g.trustedRepos[repo] = true
}

// IsTrustedRepo checks if a repository is trusted
func (g *Guard) IsTrustedRepo(repo string) bool {
	return g.trustedRepos[repo]
}
