package store

import (
	"encoding/json"
	"strings"
	"time"

	"quantex.com/qfixdpl/src/common/tracerr"
	"quantex.com/qfixdpl/src/domain"
)

func (p *Store) SaveMessage(msg domain.TradeMessage) error {
	jsonBytes, err := json.Marshal(msg.JMessage)
	if err != nil {
		return tracerr.Errorf("error marshaling j_message: %w", err)
	}

	_, err = p.db.Exec(
		"INSERT INTO qfixdpl_messages (quote_req_id, trade_id, j_message) VALUES ($1, NULLIF($2, ''), $3)",
		msg.QuoteReqID, msg.TradeID, string(jsonBytes),
	)
	if err != nil {
		return tracerr.Errorf("error inserting message: %w", err)
	}

	return nil
}

func (p *Store) SaveLog(entry domain.LogEntry) error {
	upsertStmt := `INSERT INTO qfixdpl_logs (quote_req_id, raw_msg)
		 VALUES ($1, $2)
		 ON CONFLICT (quote_req_id) DO UPDATE
		 SET raw_msg = qfixdpl_logs.raw_msg || E'\n' || EXCLUDED.raw_msg,
		     updated_at = NOW()`

	_, err := p.db.Exec(upsertStmt, entry.QuoteReqID, entry.RawMsg)
	if err != nil {
		return tracerr.Errorf("error upserting log: %w", err)
	}

	return nil
}

func (p *Store) GetTodayMessages() ([]domain.TradeMessage, error) {
	rows, err := p.db.Query(
		"SELECT id, quote_req_id, trade_id, j_message, created_at FROM qfixdpl_messages WHERE created_at >= current_date ORDER BY created_at ASC",
	)
	if err != nil {
		return nil, tracerr.Errorf("error querying today messages: %w", err)
	}
	defer rows.Close()

	var messages []domain.TradeMessage

	for rows.Next() {
		var (
			id, quoteReqID string
			tradeID        *string
			jMessageRaw    []byte
			createdAt      time.Time
		)

		if err := rows.Scan(&id, &quoteReqID, &tradeID, &jMessageRaw, &createdAt); err != nil {
			return nil, tracerr.Errorf("error scanning message row: %w", err)
		}

		var jMessage domain.FixMessageJSON
		if err := json.Unmarshal(jMessageRaw, &jMessage); err != nil {
			return nil, tracerr.Errorf("error unmarshaling j_message: %w", err)
		}

		msg := domain.TradeMessage{
			ID:         id,
			QuoteReqID: quoteReqID,
			JMessage:   jMessage,
			CreatedAt:  createdAt,
		}

		if tradeID != nil {
			msg.TradeID = *tradeID
		}

		messages = append(messages, msg)
	}

	if err := rows.Err(); err != nil {
		return nil, tracerr.Errorf("error iterating message rows: %w", err)
	}

	return messages, nil
}

func (p *Store) UpdateLogTradeID(quoteReqID, tradeID string) error {
	_, err := p.db.Exec(
		"UPDATE qfixdpl_logs SET trade_id = $1, updated_at = NOW() WHERE quote_req_id = $2",
		tradeID, quoteReqID,
	)
	if err != nil {
		return tracerr.Errorf("error updating log trade_id: %w", err)
	}

	return nil
}

func (p *Store) GetFullTradeLog(quoteReqID string) (domain.FullTradeLog, error) {
	rows, err := p.db.Query(
		"SELECT trade_id, raw_msg FROM qfixdpl_logs WHERE quote_req_id = $1", quoteReqID,
	)
	if err != nil {
		return domain.FullTradeLog{}, tracerr.Errorf("error querying dpl log: %w", err)
	}
	defer rows.Close()

	if !rows.Next() {
		return domain.FullTradeLog{}, tracerr.Errorf("no log found for quoteReqID: %s", quoteReqID)
	}

	var (
		tradeID *string
		rawMsg  string
	)

	if err := rows.Scan(&tradeID, &rawMsg); err != nil {
		return domain.FullTradeLog{}, tracerr.Errorf("error scanning dpl log row: %w", err)
	}

	result := domain.FullTradeLog{
		QuoteReqID: quoteReqID,
		DPLEntries: strings.Split(rawMsg, "\n"),
	}

	if tradeID != nil && *tradeID != "" {
		result.TradeID = *tradeID

		ptRows, err := p.db.Query(
			"SELECT raw_msg FROM qfixpt_logs WHERE trade_id = $1", *tradeID,
		)
		if err != nil {
			return domain.FullTradeLog{}, tracerr.Errorf("error querying pt logs: %w", err)
		}
		defer ptRows.Close()

		if ptRows.Next() {
			var ptRawMsg string
			if err := ptRows.Scan(&ptRawMsg); err != nil {
				return domain.FullTradeLog{}, tracerr.Errorf("error scanning pt log row: %w", err)
			}

			result.PTEntries = strings.Split(ptRawMsg, "\n")
		}
	}

	return result, nil
}

func (p *Store) GetLogsByQuoteReqID(quoteReqID string) (domain.Logs, error) {
	selectStmt := "SELECT raw_msg FROM qfixdpl_logs WHERE quote_req_id = '" + quoteReqID + "';"

	response, err := p.db.Query(selectStmt)
	if err != nil {
		return domain.Logs{}, tracerr.Errorf("error querying logs: %w", err)
	}
	defer response.Close()

	if !response.Next() {
		return domain.Logs{}, nil
	}

	var rawMsg string
	if err := response.Scan(&rawMsg); err != nil {
		return domain.Logs{}, tracerr.Errorf("error scanning log row: %w", err)
	}

	return domain.Logs{Entries: strings.Split(rawMsg, "\n")}, nil
}
