package service

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"unsafe"

	"github.com/tarm/serial"
)

// SerialPrinter 串口打印机
type SerialPrinter struct {
	Port *serial.Port
}

func NewSerialPrinter(portName string, baudRate int) (*SerialPrinter, error) {
	if baudRate <= 0 {
		baudRate = 9600
	}
	if len(portName) < 3 || portName[:3] != "COM" {
		return nil, fmt.Errorf("非串口设备: %s", portName)
	}
	c := &serial.Config{Name: portName, Baud: baudRate}
	port, err := serial.OpenPort(c)
	if err != nil {
		return nil, fmt.Errorf("打开串口失败 %s: %w", portName, err)
	}
	return &SerialPrinter{Port: port}, nil
}

func (p *SerialPrinter) Write(data []byte) error { _, err := p.Port.Write(data); return err }
func (p *SerialPrinter) Close() error            { return p.Port.Close() }

// === Windows RAW 打印机 ===

var winspool = syscall.NewLazyDLL("winspool.drv")

type DOC_INFO_1 struct {
	pDocName    *uint16
	pOutputFile *uint16
	pDataType   *uint16
}

func PrintRaw(printerName string, data []byte) error {
	namePtr, _ := syscall.UTF16PtrFromString(printerName)
	rawType, _ := syscall.UTF16PtrFromString("RAW")

	var handle syscall.Handle
	ret, _, _ := winspool.NewProc("OpenPrinterW").Call(
		uintptr(unsafe.Pointer(namePtr)),
		uintptr(unsafe.Pointer(&handle)), 0)
	if ret == 0 {
		return fmt.Errorf("OpenPrinter 失败: %s", printerName)
	}
	defer winspool.NewProc("ClosePrinter").Call(uintptr(handle))

	docName, _ := syscall.UTF16PtrFromString("Ticket")
	di := DOC_INFO_1{pDocName: docName, pDataType: rawType}

	jobID, _, _ := winspool.NewProc("StartDocPrinterW").Call(
		uintptr(handle), 1, uintptr(unsafe.Pointer(&di)))
	if jobID == 0 {
		return fmt.Errorf("StartDocPrinter 失败")
	}

	winspool.NewProc("StartPagePrinter").Call(uintptr(handle))

	var written uint32
	winspool.NewProc("WritePrinter").Call(
		uintptr(handle), uintptr(unsafe.Pointer(&data[0])),
		uintptr(len(data)), uintptr(unsafe.Pointer(&written)))

	winspool.NewProc("EndPagePrinter").Call(uintptr(handle))
	winspool.NewProc("EndDocPrinter").Call(uintptr(handle))

	return nil
}

func copyToPrinter(printerName string, data []byte) error {
	tmpFile, err := os.CreateTemp("", "tkt_*.bin")
	if err != nil {
		return err
	}
	tmpPath := tmpFile.Name()
	defer os.Remove(tmpPath)
	if _, err := tmpFile.Write(data); err != nil {
		tmpFile.Close()
		return err
	}
	tmpFile.Close()

	cmd := exec.Command("cmd", "/C", "copy", "/B", tmpPath, fmt.Sprintf("\\\\127.0.0.1\\%s", printerName))
	out, err := cmd.CombinedOutput()
	if err == nil {
		return nil
	}
	_ = out
	return PrintRaw(printerName, data)
}

var _ = os.Stderr
