package cursor

import (
	"bytes"
	"context"
	"fmt"
	"strings"

	"github.com/Clyra-AI/wrkr/core/detect"
	"github.com/Clyra-AI/wrkr/core/model"
	"gopkg.in/yaml.v3"
)

const detectorID = "cursor"

type Detector struct{}

func New() Detector { return Detector{} }

func (Detector) ID() string { return detectorID }

type cursorMCP struct {
	MCPServers map[string]struct {
		URL string `json:"url"`
	} `json:"mcpServers"`
}

type ruleFrontmatter struct {
	Description string   `yaml:"description"`
	AlwaysApply bool     `yaml:"alwaysApply"`
	Globs       []string `yaml:"globs"`
}

func (Detector) Detect(_ context.Context, scope detect.Scope, _ detect.Options) ([]model.Finding, error) {
	if err := detect.ValidateScopeRoot(scope.Root); err != nil {
		return nil, err
	}

	findings := make([]model.Finding, 0)
	if detect.FileExists(scope.Root, ".cursorrules") {
		findings = append(findings, model.Finding{
			FindingType: "tool_config",
			Severity:    model.SeverityLow,
			ToolType:    "cursor",
			Location:    ".cursorrules",
			Repo:        scope.Repo,
			Org:         fallbackOrg(scope.Org),
			Detector:    detectorID,
			Evidence: []model.Evidence{
				{Key: "deprecated_surface", Value: "true"},
				{Key: "delivery_harness", Value: "cursor_rules"},
				{Key: "resolver_ref", Value: ".cursorrules"},
			},
		})
	}

	rules, globErr := detect.Glob(scope.Root, ".cursor/rules/*.mdc")
	if globErr != nil {
		return nil, fmt.Errorf("glob cursor rules: %w", globErr)
	}
	for _, rel := range rules {
		frontmatter, parseErr := parseMDCFrontmatter(scope.Root, rel)
		if parseErr != nil {
			parseErr.Detector = detectorID
			findings = append(findings, model.Finding{
				FindingType: "parse_error",
				Severity:    model.SeverityMedium,
				ToolType:    "cursor",
				Location:    rel,
				Repo:        scope.Repo,
				Org:         fallbackOrg(scope.Org),
				Detector:    detectorID,
				ParseError:  parseErr,
			})
			continue
		}
		findings = append(findings, model.Finding{
			FindingType: "tool_config",
			Severity:    model.SeverityLow,
			ToolType:    "cursor",
			Location:    rel,
			Repo:        scope.Repo,
			Org:         fallbackOrg(scope.Org),
			Detector:    detectorID,
			Evidence: []model.Evidence{
				{Key: "always_apply", Value: fmt.Sprintf("%t", frontmatter.AlwaysApply)},
				{Key: "glob_count", Value: fmt.Sprintf("%d", len(frontmatter.Globs))},
				{Key: "delivery_harness", Value: "cursor_rules"},
				{Key: "resolver_ref", Value: rel},
			},
		})
	}

	if detect.FileExists(scope.Root, ".cursor/mcp.json") {
		var parsed cursorMCP
		if parseErr := detect.ParseJSONFileAllowUnknownFields(detectorID, scope.Root, ".cursor/mcp.json", &parsed); parseErr != nil {
			parseErr.Detector = detectorID
			findings = append(findings, model.Finding{
				FindingType: "parse_error",
				Severity:    model.SeverityMedium,
				ToolType:    "cursor",
				Location:    ".cursor/mcp.json",
				Repo:        scope.Repo,
				Org:         fallbackOrg(scope.Org),
				Detector:    detectorID,
				ParseError:  parseErr,
			})
		} else {
			findings = append(findings, model.Finding{
				FindingType: "tool_config",
				Severity:    model.SeverityLow,
				ToolType:    "cursor",
				Location:    ".cursor/mcp.json",
				Repo:        scope.Repo,
				Org:         fallbackOrg(scope.Org),
				Detector:    detectorID,
				Permissions: []string{"mcp.access"},
				Evidence: []model.Evidence{
					{Key: "mcp_server_count", Value: fmt.Sprintf("%d", len(parsed.MCPServers))},
					{Key: "delivery_harness", Value: "cursor_rules"},
					{Key: "resolver_ref", Value: ".cursor/mcp.json"},
				},
			})
		}
	}

	model.SortFindings(findings)
	return findings, nil
}

func parseMDCFrontmatter(root, rel string) (ruleFrontmatter, *model.ParseError) {
	payload, parseErr := detect.ReadFileWithinRoot(detectorID, root, rel)
	if parseErr != nil {
		return ruleFrontmatter{}, parseErr
	}
	trimmed := string(payload)
	if !strings.HasPrefix(trimmed, "---\n") {
		return ruleFrontmatter{}, nil
	}
	idx := strings.Index(trimmed[4:], "\n---\n")
	if idx < 0 {
		return ruleFrontmatter{}, &model.ParseError{Kind: "parse_error", Format: "yaml", Path: rel, Message: "missing frontmatter terminator"}
	}
	section := trimmed[4 : 4+idx]
	var out ruleFrontmatter
	decoder := yaml.NewDecoder(bytes.NewBufferString(section))
	if decodeErr := decoder.Decode(&out); decodeErr != nil {
		return ruleFrontmatter{}, &model.ParseError{Kind: "parse_error", Format: "yaml", Path: rel, Message: decodeErr.Error()}
	}
	return out, nil
}

func fallbackOrg(org string) string {
	if strings.TrimSpace(org) == "" {
		return "local"
	}
	return org
}
