package main

import (
	"errors"
	"fmt"
	"os"
)

// ========== 1. 自定义错误 + fmt.Errorf 封装 ==========
func divide(a, b float64) (float64, error) {
	if b == 0 {
		// 使用 fmt.Errorf 包装原始错误，添加上下文
		return 0, fmt.Errorf("divide by zero: cannot divide %.2f by %.2f", a, b)
	}
	return a / b, nil
}

// 更复杂的错误链（Go 1.13+ 支持 %w）
var ErrNegativeInput = errors.New("input must be non-negative")

func sqrt(x float64) (float64, error) {
	if x < 0 {
		// 使用 %w 包装错误，支持 errors.Is 和 errors.As
		return 0, fmt.Errorf("invalid input for sqrt: %w", ErrNegativeInput)
	}
	return x * x, nil // 注意：这里故意写成平方，方便测试
}

// ========== 2. defer 的典型用途 ==========
func readFile(filename string) error {
	fmt.Println("尝试打开文件:", filename)
	file, err := os.Open(filename)
	if err != nil {
		return fmt.Errorf("无法打开文件 %s: %w", filename, err)
	}
	// defer 确保文件在函数退出前关闭（即使 panic）
	defer func() {
		fmt.Println("defer: 关闭文件")
		file.Close()
	}()

	// 模拟读取
	fmt.Println("文件已打开，正在读取...")
	return nil
}

// ========== 3. panic 与 recover ==========
func riskyFunction(n int) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("recover 捕获到 panic: %v\n", r)
		}
	}()
	if n < 0 {
		panic("n 不能为负数！")
	}
	fmt.Println("riskyFunction 正常执行，n =", n)
}

// recover 只在 defer 中有效！
func noRecover() {
	// ❌ 这样写无法捕获 panic
	recover()
	panic("不会被捕获")
}

// ========== 4. 综合示例：安全计算 ==========
func safeCompute(a, b float64) {
	defer fmt.Println("safeCompute 结束\n---")

	fmt.Printf("计算 %.2f / %.2f\n", a, b)
	result, err := divide(a, b)
	if err != nil {
		fmt.Println("错误:", err)
		return
	}
	fmt.Printf("结果: %.2f\n", result)

	fmt.Println("尝试对结果开方（实际是平方）")
	sq, err := sqrt(result)
	if err != nil {
		if errors.Is(err, ErrNegativeInput) {
			fmt.Println("检测到特定错误：输入为负")
		}
		fmt.Println("sqrt 错误:", err)
		return
	}
	fmt.Printf("平方结果: %.2f\n", sq)
}

// ========== 主函数 ==========
func main() {
	fmt.Println("=== 1. 基本 error 处理 ===")
	safeCompute(10, 2)
	safeCompute(10, 0) // 触发除零错误

	fmt.Println("\n=== 2. defer 文件操作 ===")
	fileErr := readFile("/nonexistent/file.txt") // 文件不存在，触发错误
	if fileErr != nil {
		fmt.Println("文件操作错误:", fileErr)
	}
	fileErr2 := readFile("/etc/hostname") // 存在的文件（Linux 系统通常有）
	if fileErr2 != nil {
		fmt.Println("文件操作错误:", fileErr2)
	}
	fmt.Println("\n=== 3. panic 与 recover ===")
	riskyFunction(5)
	riskyFunction(-1) // 触发 panic，但被 recover 捕获

	fmt.Println("\n=== 4. 错误封装与识别 ===")
	_, err := sqrt(-4)
	if err != nil {
		fmt.Println("原始错误信息:", err)
		// 使用 errors.Is 判断是否包含特定错误
		if errors.Is(err, ErrNegativeInput) {
			fmt.Println("✅ 成功识别自定义错误 ErrNegativeInput")
		}
	}

	// ⚠️ 取消注释下面这行会 crash（recover 不在 defer 中无效）
	//noRecover()
}
