package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	goruntime "runtime"
	"strings"
	"time"

	"github.com/wailsapp/wails/v3/pkg/application"
)

// App 经 Wails v3 的 Service 绑定暴露给前端，承载只有桌面端能做的原生能力：
// 文件读写、系统"另存为"框、用系统默认应用打开、原生通知。
// v3 中不再需要注入 ctx——对话框经 application.Get().Dialog 获取。
type App struct{}

// Ping 供前端探活 Go 桥是否就绪。
func (a *App) Ping() string { return "sundynix-desktop ok" }

// SaveReportAs 弹原生"另存为"对话框，把 url 指向的报告（.docx）下载到用户选定路径。
// 返回保存路径；用户取消则返回空串。
func (a *App) SaveReportAs(url, filename string) (string, error) {
	if filename == "" {
		filename = "report.docx"
	}
	path, err := application.Get().Dialog.SaveFile().
		SetFilename(filename).
		AddFilter("Word 文档 (*.docx)", "*.docx").
		PromptForSingleSelection()
	if err != nil || path == "" {
		return "", err
	}
	if err := download(url, path); err != nil {
		return "", err
	}
	return path, nil
}

// PrintReportPage 把报告打印视图 HTML 落到临时文件，交系统默认浏览器打开（在那里 ⌘P →
// 存储为 PDF）。Wails 的 WKWebView 会把 window.open 拦成 null，前端弹打印窗那条路在壳内
// 走不通；转交系统浏览器后「前端打印出 PDF、CJK 零字体依赖」的原有优势不变。返回落盘路径。
func (a *App) PrintReportPage(filename, html string) (string, error) {
	if filename == "" {
		filename = "report"
	}
	// 文件名进过滤：主题可能含 / 之类的路径字符。
	safe := strings.Map(func(r rune) rune {
		if strings.ContainsRune(`/\:*?"<>|`, r) {
			return '_'
		}
		return r
	}, filename)
	dst := filepath.Join(os.TempDir(), "sundynix-print-"+safe+".html")
	if err := os.WriteFile(dst, []byte(html), 0o600); err != nil {
		return "", err
	}
	return dst, openInSystem(dst)
}

// Notify 弹一条系统通知（best-effort：macOS 用 osascript，其它平台暂静默）。
func (a *App) Notify(title, body string) {
	if goruntime.GOOS == "darwin" {
		script := fmt.Sprintf("display notification %q with title %q", body, title)
		_ = exec.Command("osascript", "-e", script).Start()
	}
}

// downloadClient 给下载加超时：默认 http.Get 用的 client 没有超时，
// 上游卡住的话「另存为」会永远转下去，用户只能强杀 app。
var downloadClient = &http.Client{Timeout: 3 * time.Minute}

// download 把 url 下载到 dst。失败一律不留残file：
// 中途断开会在用户选定的路径上留个半截 .docx，带着用户起的名字，以后双击打不开。
func download(url, dst string) (err error) {
	resp, err := downloadClient.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return fmt.Errorf("下载失败 HTTP %d", resp.StatusCode)
	}
	f, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer func() {
		// Close 的错误不能吞：写文件时 io.Copy 成功不代表数据落了盘，
		// flush 失败只会在 Close 上报出来——吞掉就是静默截断。
		cerr := f.Close()
		if err == nil {
			err = cerr
		}
		if err != nil {
			_ = os.Remove(dst) // 失败不留残file
		}
	}()
	_, err = io.Copy(f, resp.Body)
	return err
}

func openInSystem(path string) error {
	switch goruntime.GOOS {
	case "darwin":
		return exec.Command("open", path).Start()
	case "windows":
		return exec.Command("rundll32", "url.dll,FileProtocolHandler", path).Start()
	default:
		return exec.Command("xdg-open", path).Start()
	}
}
