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 打印机（WritePrinter API）===

var (
	winspool         = syscall.NewLazyDLL("winspool.drv")
	procOpenPrinter  = winspool.NewProc("OpenPrinterW")
	procClosePrinter = winspool.NewProc("ClosePrinter")
	procStartDoc     = winspool.NewProc("StartDocPrinterW")
	procStartPage    = winspool.NewProc("StartPagePrinter")
	procWritePrinter = winspool.NewProc("WritePrinter")
	procEndPage      = winspool.NewProc("EndPagePrinter")
	procEndDoc       = winspool.NewProc("EndDocPrinter")
)

type PRINTER_DEFAULTS struct {
	pDatatype     *uint16
	pDevMode      uintptr
	DesiredAccess uint32
}

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")

	pd := PRINTER_DEFAULTS{
		pDatatype:     rawType,
		pDevMode:      0,
		DesiredAccess: 0x000F0000 | 0x00000008, // PRINTER_ACCESS_USE | PRINTER_ACCESS_ADMINISTER
	}

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

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

	procStartDoc.Call(uintptr(handle), 1, uintptr(unsafe.Pointer(&di)))
	procStartPage.Call(uintptr(handle))

	var written uint32
	procWritePrinter.Call(uintptr(handle), uintptr(unsafe.Pointer(&data[0])),
		uintptr(len(data)), uintptr(unsafe.Pointer(&written)))

	procEndPage.Call(uintptr(handle))
	procEndDoc.Call(uintptr(handle))

	return nil
}

// copyToPrinter 通过临时文件 + COPY /B 到打印机共享发送 RAW 数据
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()

	// 尝试 COPY /B file \\127.0.0.1\share_name
	// 先查共享名
	shareName := printerName
	for _, path := range []string{
		fmt.Sprintf("\\\\127.0.0.1\\%s", shareName),
		fmt.Sprintf("\\\\localhost\\%s", shareName),
	} {
		cmd := exec.Command("cmd", "/C", "copy", "/B", tmpPath, path)
		output, err := cmd.CombinedOutput()
		if err == nil {
			return nil
		}
		// 打印失败，尝试下一个
		_ = output
	}

	// 最后回退 WritePrinter API
	return PrintRaw(printerName, data)
}

var _ = os.Stderr
