package postgresql

import (
	"bytes"
	"encoding/binary"
	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/service/matcher/packet"
	"git.loafle.net/overflow/overflow_probe/discovery/discovery/types"
)

const (
	RESPONSE_TYPE_ERR uint8 = 0x45
)

type pgsql struct {
	Len              uint32
	MessageType      uint16
	_                uint16
	Name             [5]byte
	NameValue        byte
	Db               [9]byte
	DBValue          byte
	Encoding         [16]byte
	EncodingValue    [5]byte
	DateStyle        [10]byte
	DateStyleValue   [4]byte
	TimeZone         [9]byte
	TimeZoneValue    [11]byte
	ExtraDigits      [19]byte
	ExtraDigitsValue uint16
	End              byte
}

type pgsqlErrResponse struct {
	ResponseType uint8
	Len          [4]byte
	Severity     [6]byte
	_            byte
	Code         [6]byte
	_            byte
	Message      [53]byte
}

type PostgreSQLMatcher struct {
	packets []*packet.Packet
}

func NewPostgreSQLMatcher() *PostgreSQLMatcher {

	pgSqlMatcher := &PostgreSQLMatcher{}

	pg := pgsql{}

	pg.Len = 0x00000065
	pg.MessageType = 0x0003

	var name [5]byte
	copy(name[:], "user")
	pg.Name = name
	pg.NameValue = 0x00

	var db [9]byte
	copy(db[:], "database")
	pg.Db = db
	pg.DBValue = 0x00

	var encoding [16]byte
	copy(encoding[:], "client_encoding")
	pg.Encoding = encoding

	var encodingValue [5]byte
	copy(encodingValue[:], "UTF8")
	pg.EncodingValue = encodingValue

	var dateStyle [10]byte
	copy(dateStyle[:], "DateStyle")
	pg.DateStyle = dateStyle

	var dateStyleValue [4]byte
	copy(dateStyleValue[:], "ISO")
	pg.DateStyleValue = dateStyleValue

	var timeZone [9]byte
	copy(timeZone[:], "TimeZone")
	pg.TimeZone = timeZone

	var timeZoneValue [11]byte
	copy(timeZoneValue[:], "Asia/Seoul")
	pg.TimeZoneValue = timeZoneValue

	var extraDigit [19]byte
	copy(extraDigit[:], "extra_float_digits")
	pg.ExtraDigits = extraDigit

	pg.ExtraDigitsValue = 0x3200

	writer := new(bytes.Buffer)
	binary.Write(writer, binary.BigEndian, pg)

	pgSqlMatcher.packets = append(pgSqlMatcher.packets, packet.NewPacket(writer.Bytes(), writer.Len()))

	return pgSqlMatcher
}

func (t *PostgreSQLMatcher) ServiceName() string {
	return "PostgreSQL"
}

func (t *PostgreSQLMatcher) PacketCount() int {
	return len(t.packets)
}
func (t *PostgreSQLMatcher) Packet(index int) *packet.Packet {
	return t.packets[index]
}

func (t *PostgreSQLMatcher) IsError(index int, packet *packet.Packet, info *types.ServiceScanInfo) bool {
	return false
}

func (t *PostgreSQLMatcher) IsNoResponse(index int) bool {
	return false
}

func (t *PostgreSQLMatcher) IsPrePacket() bool {
	return false
}

func (t *PostgreSQLMatcher) Match(index int, packet *packet.Packet, info *types.ServiceScanInfo) bool {

	if packet == nil {
		return false
	}

	reader := new(bytes.Buffer)
	reader.Write(packet.Buffer)

	pg := pgsqlErrResponse{}
	if err := binary.Read(reader, binary.BigEndian, &pg); err != nil {
		return false
	}

	if pg.ResponseType != RESPONSE_TYPE_ERR {
		return false
	}

	if string(pg.Severity[:]) != "SFATAL" {
		return false
	}

	if string(pg.Code[:]) != "C28000" {
		return false
	}

	return true

}
