package analyzers

import (
	"go/ast"
	"go/token"
	"strings"

	"golang.org/x/tools/go/analysis"
)

// HIPAACompletenessAnalyzer flags any switch or if-else statement that checks
// compliance_mode == "hipaa" but doesn't verify all six hard-required fields.
// This prevents partial HIPAA enforcement from silently passing.
var HIPAACompletenessAnalyzer = &analysis.Analyzer{
	Name: "hippacomplete",
	Doc:  "flags incomplete HIPAA compliance mode checks",
	Run:  runHIPAACompleteness,
}

// hipaaRequiredFields are the six fields that must be checked in HIPAA mode
var hipaaRequiredFields = []string{
	"ConfigHash",
	"SandboxSeccomp",
	"SandboxNoNewPrivs",
	"SandboxNetworkMode",
	"MaxWorkers",
	"ComplianceMode",
}

func runHIPAACompleteness(pass *analysis.Pass) (interface{}, error) {
	for _, file := range pass.Files {
		ast.Inspect(file, func(n ast.Node) bool {
			// Look for if statements
			ifStmt, ok := n.(*ast.IfStmt)
			if ok {
				checkHIPAACondition(pass, ifStmt.Cond, ifStmt.Body)
				return true
			}

			// Look for switch statements
			switchStmt, ok := n.(*ast.SwitchStmt)
			if ok {
				checkHIPAASwitch(pass, switchStmt)
				return true
			}

			return true
		})
	}
	return nil, nil
}

// checkHIPAACondition checks if an if condition is checking for HIPAA mode
func checkHIPAACondition(pass *analysis.Pass, cond ast.Expr, body *ast.BlockStmt) {
	// Check if condition checks for "hipaa" string
	if !containsHIPAACheck(cond) {
		return
	}

	// Check what fields are accessed in the body
	checkedFields := extractCheckedFields(body)

	// Report missing fields
	var missing []string
	for _, required := range hipaaRequiredFields {
		found := false
		for _, checked := range checkedFields {
			if strings.EqualFold(checked, required) {
				found = true
				break
			}
		}
		if !found {
			missing = append(missing, required)
		}
	}

	// If checking HIPAA mode but not all required fields, report it
	if len(missing) > 0 && len(missing) < len(hipaaRequiredFields) {
		// Partial check detected - this is the problematic case
		pass.Reportf(body.Pos(),
			"HIPAA compliance mode check is incomplete - missing required fields: %v",
			missing)
	}
}

// checkHIPAASwitch checks switch statements for HIPAA mode handling
func checkHIPAASwitch(pass *analysis.Pass, switchStmt *ast.SwitchStmt) {
	// Check if the switch tag checks compliance mode
	if switchStmt.Tag == nil {
		return
	}

	if !isComplianceModeCheck(switchStmt.Tag) {
		return
	}

	// Find the "hipaa" case
	var hipaaCase *ast.CaseClause
	for _, stmt := range switchStmt.Body.List {
		caseClause, ok := stmt.(*ast.CaseClause)
		if !ok {
			continue
		}

		// Check if this case is for "hipaa"
		for _, val := range caseClause.List {
			if isHipaaString(val) {
				hipaaCase = caseClause
				break
			}
		}
		if hipaaCase != nil {
			break
		}
	}

	if hipaaCase == nil {
		return
	}

	// Check what fields are accessed in the hipaa case body
	checkedFields := extractCheckedFieldsFromStmts(hipaaCase.Body)

	// Report missing fields
	var missing []string
	for _, required := range hipaaRequiredFields {
		found := false
		for _, checked := range checkedFields {
			if strings.EqualFold(checked, required) {
				found = true
				break
			}
		}
		if !found {
			missing = append(missing, required)
		}
	}

	if len(missing) > 0 && len(missing) < len(hipaaRequiredFields) {
		pass.Reportf(hipaaCase.Pos(),
			"HIPAA case is incomplete - missing required field checks: %v",
			missing)
	}
}

// containsHIPAACheck checks if an expression contains a check for "hipaa"
func containsHIPAACheck(expr ast.Expr) bool {
	switch e := expr.(type) {
	case *ast.BinaryExpr:
		// Check for == "hipaa" or != "hipaa"
		if isHipaaString(e.X) || isHipaaString(e.Y) {
			return true
		}
		// Recursively check both sides
		return containsHIPAACheck(e.X) || containsHIPAACheck(e.Y)
	case *ast.CallExpr:
		// Check for strings.EqualFold(x, "hipaa") or similar
		return containsHipaaInCall(e)
	}
	return false
}

// isHipaaString checks if an expression is the string literal "hipaa"
func isHipaaString(expr ast.Expr) bool {
	lit, ok := expr.(*ast.BasicLit)
	if !ok {
		return false
	}
	return lit.Kind == token.STRING && (lit.Value == `"hipaa"` || lit.Value == `"HIPAA"`)
}

// isComplianceModeCheck checks if an expression is accessing compliance_mode
func isComplianceModeCheck(expr ast.Expr) bool {
	switch e := expr.(type) {
	case *ast.SelectorExpr:
		return strings.EqualFold(e.Sel.Name, "ComplianceMode") ||
			strings.EqualFold(e.Sel.Name, "compliance_mode")
	case *ast.Ident:
		return strings.EqualFold(e.Name, "complianceMode") ||
			strings.EqualFold(e.Name, "compliance_mode")
	}
	return false
}

// containsHipaaInCall checks if a function call contains "hipaa" as an argument
func containsHipaaInCall(call *ast.CallExpr) bool {
	for _, arg := range call.Args {
		if isHipaaString(arg) {
			return true
		}
	}
	return false
}

// extractCheckedFields extracts field names that are accessed in a block
func extractCheckedFields(block *ast.BlockStmt) []string {
	if block == nil {
		return nil
	}
	return extractCheckedFieldsFromStmts(block.List)
}

// extractCheckedFieldsFromStmts extracts field names from a list of statements
func extractCheckedFieldsFromStmts(stmts []ast.Stmt) []string {
	var fields []string

	for _, stmt := range stmts {
		ast.Inspect(stmt, func(n ast.Node) bool {
			// Look for selector expressions (field access)
			sel, ok := n.(*ast.SelectorExpr)
			if !ok {
				return true
			}

			fieldName := sel.Sel.Name
			fields = append(fields, fieldName)
			return true
		})
	}

	return fields
}
