package codex

import (
	"context"
	"fmt"
	"strings"

	"github.com/Clyra-AI/wrkr/core/detect"
	"github.com/Clyra-AI/wrkr/core/model"
)

const detectorID = "codex"

type Detector struct{}

func New() Detector { return Detector{} }

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

type configModel struct {
	SandboxMode    string `toml:"sandbox_mode" yaml:"sandbox_mode"`
	ApprovalPolicy string `toml:"approval_policy" yaml:"approval_policy"`
	NetworkAccess  bool   `toml:"network_access" yaml:"network_access"`
}

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)

	for _, rel := range []string{"AGENTS.md", "AGENTS.override.md"} {
		exists, parseErr := detect.FileExistsWithinRoot(detectorID, scope.Root, rel)
		if parseErr != nil {
			findings = append(findings, model.Finding{
				FindingType: "parse_error",
				Severity:    model.SeverityMedium,
				ToolType:    "codex",
				Location:    rel,
				Repo:        scope.Repo,
				Org:         fallbackOrg(scope.Org),
				Detector:    detectorID,
				ParseError:  parseErr,
				Remediation: "Move Codex instruction files inside the selected repository root.",
			})
			continue
		}
		if !exists {
			continue
		}
		findings = append(findings, model.Finding{
			FindingType: "tool_config",
			Severity:    model.SeverityLow,
			ToolType:    "codex",
			Location:    rel,
			Repo:        scope.Repo,
			Org:         fallbackOrg(scope.Org),
			Detector:    detectorID,
			Evidence: []model.Evidence{
				{Key: "delivery_harness", Value: "codex_cli"},
				{Key: "resolver_ref", Value: rel},
			},
		})
	}

	if exists, parseErr := detect.FileExistsWithinRoot(detectorID, scope.Root, ".codex/config.toml"); parseErr != nil {
		findings = append(findings, model.Finding{
			FindingType: "parse_error",
			Severity:    model.SeverityMedium,
			ToolType:    "codex",
			Location:    ".codex/config.toml",
			Repo:        scope.Repo,
			Org:         fallbackOrg(scope.Org),
			Detector:    detectorID,
			ParseError:  parseErr,
			Remediation: "Keep Codex configuration inside the selected repository root.",
		})
	} else if exists {
		items, parseErr := parseConfig(scope, ".codex/config.toml", "toml")
		if parseErr != nil {
			findings = append(findings, *parseErr)
		} else {
			findings = append(findings, items)
		}
	}
	if exists, parseErr := detect.FileExistsWithinRoot(detectorID, scope.Root, ".codex/config.yaml"); parseErr != nil {
		findings = append(findings, model.Finding{
			FindingType: "parse_error",
			Severity:    model.SeverityMedium,
			ToolType:    "codex",
			Location:    ".codex/config.yaml",
			Repo:        scope.Repo,
			Org:         fallbackOrg(scope.Org),
			Detector:    detectorID,
			ParseError:  parseErr,
			Remediation: "Keep Codex configuration inside the selected repository root.",
		})
	} else if exists {
		items, parseErr := parseConfig(scope, ".codex/config.yaml", "yaml")
		if parseErr != nil {
			findings = append(findings, *parseErr)
		} else {
			findings = append(findings, items)
		}
	}

	model.SortFindings(findings)
	return findings, nil
}

func parseConfig(scope detect.Scope, rel, format string) (model.Finding, *model.Finding) {
	var parsed configModel
	var parseErr *model.ParseError
	switch format {
	case "toml":
		parseErr = detect.ParseTOMLFileAllowUnknownFields(detectorID, scope.Root, rel, &parsed)
	case "yaml":
		parseErr = detect.ParseYAMLFileAllowUnknownFields(detectorID, scope.Root, rel, &parsed)
	default:
		parseErr = &model.ParseError{Kind: "parse_error", Format: format, Path: rel, Detector: detectorID, Message: "unsupported format"}
	}
	if parseErr != nil {
		parseErr.Detector = detectorID
		finding := model.Finding{
			FindingType: "parse_error",
			Severity:    model.SeverityMedium,
			ToolType:    "codex",
			Location:    rel,
			Repo:        scope.Repo,
			Org:         fallbackOrg(scope.Org),
			Detector:    detectorID,
			ParseError:  parseErr,
			Remediation: "Fix malformed Codex configuration.",
		}
		return model.Finding{}, &finding
	}

	permissions := make([]string, 0, 3)
	if strings.EqualFold(parsed.SandboxMode, "danger-full-access") {
		permissions = append(permissions, "filesystem.write")
	}
	if parsed.NetworkAccess {
		permissions = append(permissions, "network.access")
	}
	if strings.EqualFold(parsed.ApprovalPolicy, "never") {
		permissions = append(permissions, "proc.exec")
	}

	return model.Finding{
		FindingType: "tool_config",
		Severity:    model.SeverityLow,
		ToolType:    "codex",
		Location:    rel,
		Repo:        scope.Repo,
		Org:         fallbackOrg(scope.Org),
		Detector:    detectorID,
		Permissions: permissions,
		Evidence: []model.Evidence{
			{Key: "delivery_harness", Value: "codex_cli"},
			{Key: "sandbox_mode", Value: parsed.SandboxMode},
			{Key: "approval_policy", Value: parsed.ApprovalPolicy},
			{Key: "network_access", Value: fmt.Sprintf("%t", parsed.NetworkAccess)},
			{Key: "resolver_ref", Value: rel},
			{Key: "sandbox_gate", Value: "sandbox_mode:" + strings.TrimSpace(parsed.SandboxMode)},
			{Key: "validation_requirement", Value: "respect_codex_sandbox_mode"},
		},
	}, nil
}

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