package service

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/chromedp/cdproto/page"
	"github.com/chromedp/chromedp"
	"github.com/zeromicro/go-zero/core/logx"
)

// ReportPDFService 负责根据订单信息渲染前端报告页面并生成 PDF。
type ReportPDFService struct {
	baseReportURL string
	outputDir     string
}

func NewReportPDFService(baseReportURL, outputDir string) *ReportPDFService {
	return &ReportPDFService{
		baseReportURL: baseReportURL,
		outputDir:     outputDir,
	}
}

// GenerateReportPDF 根据 orderId / orderNo 生成报告 PDF。
// orderId 与 orderNo 至少需要一个非空，否则返回错误。
func (s *ReportPDFService) GenerateReportPDF(ctx context.Context, orderId, orderNo string) ([]byte, error) {
	if orderId == "" && orderNo == "" {
		return nil, fmt.Errorf("orderId 和 orderNo 不能同时为空")
	}

	reportURL := s.buildReportURL(orderId, orderNo)
	logx.WithContext(ctx).Infof("GenerateReportPDF, reportURL=%s", reportURL)

	// 为单次渲染创建 chromedp 上下文，并设置超时。
	// 如需更高性能，可在外层维护一个共享的 chromedp 池。
	ctxt, cancel := chromedp.NewContext(ctx)
	defer cancel()

	ctxt, timeoutCancel := context.WithTimeout(ctxt, 40*time.Second)
	defer timeoutCancel()

	var pdfBuf []byte
	err := chromedp.Run(ctxt,
		chromedp.Navigate(reportURL),
		// 等待前端标记报告渲染完成（ReportPC.vue 中设置 window.__REPORT_READY__ = true）
		chromedp.Poll(`window.__REPORT_READY__ === true`, nil, chromedp.WithPollingInterval(200*time.Millisecond)),
		chromedp.ActionFunc(func(ctx context.Context) error {
			buf, _, pdfErr := page.PrintToPDF().
				WithPrintBackground(true).
				WithMarginTop(0).
				WithMarginBottom(0).
				WithMarginLeft(0).
				WithMarginRight(0).
				Do(ctx)
			if pdfErr != nil {
				return pdfErr
			}
			pdfBuf = buf
			return nil
		}),
	)
	if err != nil {
		return nil, fmt.Errorf("使用 chromedp 生成报告 PDF 失败: %w", err)
	}

	return pdfBuf, nil
}

// GetOrGenerateReportPDF 先从本地缓存读取，若不存在则生成并写入磁盘。
func (s *ReportPDFService) GetOrGenerateReportPDF(ctx context.Context, orderId, orderNo string) ([]byte, error) {
	if orderId == "" && orderNo == "" {
		return nil, fmt.Errorf("orderId 和 orderNo 不能同时为空")
	}

	// 优先使用订单ID作为文件名，否则退回订单号
	fileKey := orderId
	if fileKey == "" {
		fileKey = orderNo
	}
	fileName := fmt.Sprintf("%s.pdf", fileKey)
	if s.outputDir == "" {
		// 未配置缓存目录，则每次都生成但不落盘
		return s.GenerateReportPDF(ctx, orderId, orderNo)
	}

	fullPath := filepath.Join(s.outputDir, fileName)

	// 如果文件已存在且非空，直接读取返回
	if info, err := os.Stat(fullPath); err == nil && info.Size() > 0 {
		data, readErr := os.ReadFile(fullPath)
		if readErr == nil {
			logx.WithContext(ctx).Infof("命中报告 PDF 缓存: %s", fullPath)
			return data, nil
		}
	}

	// 缓存未命中或读取失败，重新生成
	pdfBytes, genErr := s.GenerateReportPDF(ctx, orderId, orderNo)
	if genErr != nil {
		return nil, genErr
	}

	// 尝试写入缓存（失败不影响主流程）
	if mkErr := os.MkdirAll(s.outputDir, 0o755); mkErr == nil {
		if writeErr := os.WriteFile(fullPath, pdfBytes, 0o644); writeErr != nil {
			logx.WithContext(ctx).Errorf("写入报告 PDF 缓存失败: path=%s, err=%v", fullPath, writeErr)
		} else {
			logx.WithContext(ctx).Infof("生成并缓存报告 PDF 成功: %s", fullPath)
		}
	} else {
		logx.WithContext(ctx).Errorf("创建报告 PDF 缓存目录失败: dir=%s, err=%v", s.outputDir, mkErr)
	}

	return pdfBytes, nil
}

func (s *ReportPDFService) buildReportURL(orderId, orderNo string) string {
	// /in-webview 对外的 PC 报告路由，需与前端路由保持一致，例如 /report-pc
	// 这里假设为: {baseReportURL}/report-pc?order_id=xxx&out_trade_no=xxx
	// baseReportURL 通过配置注入，例如 https://your-domain/in-webview
	query := ""
	if orderId != "" {
		query += "order_id=" + orderId
	}
	if orderNo != "" {
		if query != "" {
			query += "&"
		}
		query += "out_trade_no=" + orderNo
	}
	// 标记为 PDF 渲染模式，前端可据此调用免登录接口
	if query != "" {
		query += "&"
	}
	query += "pdf=1"

	return fmt.Sprintf("%s/report-pc?%s", s.baseReportURL, query)
}
