package fix

import (
	"log/slog"

	"quantex.com/qfixpt/quickfix"
	"quantex.com/qfixpt/quickfix/gen/fix50sp2/allocationreport"
	"quantex.com/qfixpt/quickfix/gen/fix50sp2/confirmation"
	"quantex.com/qfixpt/quickfix/gen/fix50sp2/tradecapturereport"
	"quantex.com/qfixpt/quickfix/gen/tag"
	"quantex.com/qfixpt/src/domain"
)

type application struct {
	router               *quickfix.MessageRouter
	notifier             domain.Notifier
	onLogon              func(quickfix.SessionID)
	onLogout             func(quickfix.SessionID)
	onTradeCaptureReport func(tradecapturereport.TradeCaptureReport, quickfix.SessionID)
	onAllocationReport   func(allocationreport.AllocationReport, quickfix.SessionID)
	onConfirmation       func(confirmation.Confirmation, quickfix.SessionID)
	onRawMessage         func(direction string, msg *quickfix.Message)
}

func newApplication(n domain.Notifier) *application {
	app := &application{
		router:   quickfix.NewMessageRouter(),
		notifier: n,
	}

	app.router.AddRoute(tradecapturereport.Route(app.handleTradeCaptureReport))
	app.router.AddRoute(allocationreport.Route(app.handleAllocationReport))
	app.router.AddRoute(confirmation.Route(app.handleConfirmation))

	return app
}

func (a *application) OnCreate(sessionID quickfix.SessionID) {
	slog.Info("FIX session created", "session", sessionID.String())
}

func (a *application) OnLogon(sessionID quickfix.SessionID) {
	slog.Info("FIX session logged on", "session", sessionID.String())
	if a.onLogon != nil {
		a.onLogon(sessionID)
	}
}

func (a *application) OnLogout(sessionID quickfix.SessionID) {
	slog.Info("FIX session logged out", "session", sessionID.String())

	go a.notifier.SendMsg(domain.MessageChannelError, "Logout", domain.MessageStatusWarning, nil)

	if a.onLogout != nil {
		a.onLogout(sessionID)
	}
}

func (a *application) ToAdmin(_ *quickfix.Message, _ quickfix.SessionID) {}

func (a *application) ToApp(msg *quickfix.Message, _ quickfix.SessionID) error {
	if a.onRawMessage != nil {
		a.onRawMessage("OUT", msg)
	}

	return nil
}

func (a *application) FromAdmin(_ *quickfix.Message, _ quickfix.SessionID) quickfix.MessageRejectError {
	return nil
}

func (a *application) FromApp(msg *quickfix.Message, sessionID quickfix.SessionID) quickfix.MessageRejectError {
	if a.onRawMessage != nil {
		a.onRawMessage("IN", msg)
	}

	beginString, _ := msg.Header.GetBytes(tag.BeginString)
	msgType, _ := msg.Header.GetBytes(tag.MsgType)

	var applVerID quickfix.FIXString
	msg.Header.GetField(tag.ApplVerID, &applVerID)

	slog.Info("FIX FromApp received",
		"beginString", string(beginString),
		"msgType", string(msgType),
		"applVerID", string(applVerID),
		"session", sessionID.String(),
		"rawMsg", msg.String(),
	)

	rejErr := a.router.Route(msg, sessionID)
	if rejErr != nil {
		slog.Error("FIX FromApp routing failed",
			"msgType", string(msgType),
			"error", rejErr.Error(),
			"isBusinessReject", rejErr.IsBusinessReject(),
		)
	}

	return rejErr
}

func (a *application) handleTradeCaptureReport(msg tradecapturereport.TradeCaptureReport, sessionID quickfix.SessionID) quickfix.MessageRejectError {
	tradeReportID, _ := msg.GetTradeReportID()

	slog.Info("TradeCaptureReport received",
		"tradeReportID", tradeReportID,
		"session", sessionID.String(),
	)

	if a.onTradeCaptureReport != nil {
		a.onTradeCaptureReport(msg, sessionID)
	}

	return nil
}

func (a *application) handleAllocationReport(msg allocationreport.AllocationReport, sessionID quickfix.SessionID) quickfix.MessageRejectError {
	allocReportID, _ := msg.GetAllocReportID()

	slog.Info("AllocationReport received",
		"allocReportID", allocReportID,
		"session", sessionID.String(),
	)

	if a.onAllocationReport != nil {
		a.onAllocationReport(msg, sessionID)
	}

	return nil
}

func (a *application) handleConfirmation(msg confirmation.Confirmation, sessionID quickfix.SessionID) quickfix.MessageRejectError {
	confirmID, _ := msg.GetConfirmID()

	slog.Info("Confirmation received",
		"confirmID", confirmID,
		"session", sessionID.String(),
	)

	if a.onConfirmation != nil {
		a.onConfirmation(msg, sessionID)
	}

	return nil
}
