package gb32960

import (
	"encoding/binary"
	"errors"
	"time"

	"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
)

var ErrResponseFrameTooShort = errors.New("gb32960 response frame too short")

const (
	responseSuccess = byte(0x01)
	responseError   = byte(0x02)
	responseCommand = byte(0xfe)
	encryptNone     = byte(0x01)
)

// AutoResponse builds the protocol ACK for accepted upstream GB/T 32960 frames.
// The ACK is written only after the caller has durably published the frame.
func AutoResponse(raw []byte, env envelope.FrameEnvelope) ([]byte, bool, error) {
	if env.ParseStatus == envelope.ParseBadFrame {
		return nil, false, nil
	}
	if len(raw) < headerLen+1 {
		return nil, false, ErrResponseFrameTooShort
	}
	if raw[3] != responseCommand {
		return nil, false, nil
	}
	command := raw[2]
	if !shouldRespond(command) {
		return nil, false, nil
	}

	body := []byte(nil)
	if timestamp := responseTime(raw, command); !timestamp.IsZero() {
		body = encodeGBTime(timestamp)
	}
	responseFlag := responseSuccess
	if command == 0x05 && env.AuthenticationEnforced && env.AuthenticationStatus != "accepted" {
		responseFlag = responseError
	}
	return buildResponse(raw[0], command, responseFlag, raw[4:21], body), true, nil
}

func shouldRespond(command byte) bool {
	switch command {
	case 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08:
		return true
	default:
		return false
	}
}

func responseTime(raw []byte, command byte) time.Time {
	bodyLen := len(raw) - headerLen - 1
	if bodyLen >= 6 {
		return parseGBTime(raw[headerLen : headerLen+6])
	}
	return time.Now().In(time.FixedZone("Asia/Shanghai", 8*3600))
}

func encodeGBTime(t time.Time) []byte {
	local := t.In(time.FixedZone("Asia/Shanghai", 8*3600))
	return []byte{
		byte(local.Year() - 2000),
		byte(local.Month()),
		byte(local.Day()),
		byte(local.Hour()),
		byte(local.Minute()),
		byte(local.Second()),
	}
}

func buildResponse(start byte, command byte, responseFlag byte, vin17 []byte, body []byte) []byte {
	out := make([]byte, 0, headerLen+len(body)+1)
	out = append(out, start, start, command, responseFlag)
	out = append(out, vin17[:17]...)
	out = append(out, encryptNone, 0, 0)
	binary.BigEndian.PutUint16(out[22:24], uint16(len(body)))
	out = append(out, body...)
	return append(out, bcc(out[2:]))
}
