package store

import (
	"fmt"
	"strings"
	"time"

	"gorm.io/gorm"
)

// HistorySummary comprehensive trading history for AI context
type HistorySummary struct {
	TotalTrades    int     `json:"total_trades"`
	WinRate        float64 `json:"win_rate"`
	TotalPnL       float64 `json:"total_pnl"`
	AvgTradeReturn float64 `json:"avg_trade_return"`

	BestSymbols  []SymbolStats `json:"best_symbols"`
	WorstSymbols []SymbolStats `json:"worst_symbols"`

	LongWinRate  float64 `json:"long_win_rate"`
	ShortWinRate float64 `json:"short_win_rate"`
	LongPnL      float64 `json:"long_pnl"`
	ShortPnL     float64 `json:"short_pnl"`

	AvgHoldingMins float64 `json:"avg_holding_mins"`
	BestHoldRange  string  `json:"best_hold_range"`

	RecentWinRate float64 `json:"recent_win_rate"`
	RecentPnL     float64 `json:"recent_pnl"`

	CurrentStreak int `json:"current_streak"`
	MaxWinStreak  int `json:"max_win_streak"`
	MaxLoseStreak int `json:"max_lose_streak"`
}

// GetHistorySummary generates comprehensive AI context summary
func (s *PositionStore) GetHistorySummary(traderID string) (*HistorySummary, error) {
	summary := &HistorySummary{}

	// Baseline 0 is fine here: the summary only reads counts/win-rate/PnL, not
	// the drawdown that depends on it.
	fullStats, err := s.GetFullStats(traderID, 0)
	if err != nil {
		return nil, err
	}
	summary.TotalTrades = fullStats.TotalTrades
	summary.WinRate = fullStats.WinRate
	summary.TotalPnL = fullStats.TotalPnL
	if fullStats.TotalTrades > 0 {
		summary.AvgTradeReturn = fullStats.TotalPnL / float64(fullStats.TotalTrades)
	}

	symbolStats, _ := s.GetSymbolStats(traderID, 20)
	if len(symbolStats) > 0 {
		for i := 0; i < len(symbolStats) && i < 3; i++ {
			if symbolStats[i].TotalPnL > 0 {
				summary.BestSymbols = append(summary.BestSymbols, symbolStats[i])
			}
		}
		for i := len(symbolStats) - 1; i >= 0 && len(summary.WorstSymbols) < 3; i-- {
			if symbolStats[i].TotalPnL < 0 {
				summary.WorstSymbols = append(summary.WorstSymbols, symbolStats[i])
			}
		}
	}

	dirStats, _ := s.GetDirectionStats(traderID)
	for _, d := range dirStats {
		if d.Side == "LONG" {
			summary.LongWinRate = d.WinRate
			summary.LongPnL = d.TotalPnL
		} else if d.Side == "SHORT" {
			summary.ShortWinRate = d.WinRate
			summary.ShortPnL = d.TotalPnL
		}
	}

	holdStats, _ := s.GetHoldingTimeStats(traderID)
	var bestHoldWinRate float64
	for _, h := range holdStats {
		if h.WinRate > bestHoldWinRate && h.TradeCount >= 3 {
			bestHoldWinRate = h.WinRate
			summary.BestHoldRange = h.Range
		}
	}

	// Calculate average holding time
	var positions []TraderPosition
	s.db.Where("trader_id = ? AND status = ? AND exit_time > 0", traderID, "CLOSED").Find(&positions)
	if len(positions) > 0 {
		var totalMins float64
		for _, pos := range positions {
			if pos.ExitTime > 0 {
				totalMins += float64(pos.ExitTime-pos.EntryTime) / 60000.0 // ms to minutes
			}
		}
		summary.AvgHoldingMins = totalMins / float64(len(positions))
	}

	// Recent 20 trades
	var recent []TraderPosition
	s.db.Where("trader_id = ? AND status = ?", traderID, "CLOSED").
		Order("exit_time DESC").Limit(20).Find(&recent)
	for _, pos := range recent {
		summary.RecentPnL += pos.RealizedPnL
		if pos.RealizedPnL > 0 {
			summary.RecentWinRate++
		}
	}
	if len(recent) > 0 {
		summary.RecentWinRate = summary.RecentWinRate / float64(len(recent)) * 100
	}

	// Calculate streaks
	s.calculateStreaks(traderID, summary)

	return summary, nil
}

// calculateStreaks calculates win/loss streaks
func (s *PositionStore) calculateStreaks(traderID string, summary *HistorySummary) {
	var positions []TraderPosition
	err := s.db.Where("trader_id = ? AND status = ?", traderID, "CLOSED").
		Order("exit_time DESC").
		Find(&positions).Error
	if err != nil || len(positions) == 0 {
		return
	}

	var currentStreak, maxWin, maxLose int
	var prevWin *bool
	isFirst := true

	for _, pos := range positions {
		isWin := pos.RealizedPnL > 0

		if isFirst {
			if isWin {
				currentStreak = 1
			} else {
				currentStreak = -1
			}
			isFirst = false
		}

		if prevWin == nil {
			prevWin = &isWin
		} else if *prevWin == isWin {
			if isWin {
				currentStreak++
				if currentStreak > maxWin {
					maxWin = currentStreak
				}
			} else {
				currentStreak--
				if -currentStreak > maxLose {
					maxLose = -currentStreak
				}
			}
		} else {
			if isWin {
				currentStreak = 1
			} else {
				currentStreak = -1
			}
			*prevWin = isWin
		}
	}

	summary.CurrentStreak = currentStreak
	summary.MaxWinStreak = maxWin
	summary.MaxLoseStreak = maxLose
}

// ClosedPnLRecord represents a closed position record from exchange
// All time fields use int64 millisecond timestamps (UTC)
type ClosedPnLRecord struct {
	Symbol      string
	Side        string
	EntryPrice  float64
	ExitPrice   float64
	Quantity    float64
	RealizedPnL float64
	Fee         float64
	Leverage    int
	EntryTime   int64 // Unix milliseconds UTC
	ExitTime    int64 // Unix milliseconds UTC
	OrderID     string
	CloseType   string
	ExchangeID  string
}

// CreateFromClosedPnL creates a closed position record from exchange data
func (s *PositionStore) CreateFromClosedPnL(traderID, exchangeID, exchangeType string, record *ClosedPnLRecord) (bool, error) {
	if record.Symbol == "" {
		return false, nil
	}

	side := strings.ToUpper(record.Side)
	if side == "LONG" || side == "BUY" {
		side = "LONG"
	} else if side == "SHORT" || side == "SELL" {
		side = "SHORT"
	} else {
		return false, nil
	}

	if record.Quantity <= 0 || record.ExitPrice <= 0 || record.EntryPrice <= 0 {
		return false, nil
	}

	exchangePositionID := record.ExchangeID
	if exchangePositionID == "" {
		exchangePositionID = fmt.Sprintf("%s_%s_%d_%.8f", record.Symbol, side, record.ExitTime, record.RealizedPnL)
	}

	exists, err := s.ExistsWithExchangePositionID(exchangeID, exchangePositionID)
	if err != nil {
		return false, err
	}
	if exists {
		return false, nil
	}

	exitTimeMs := record.ExitTime
	entryTimeMs := record.EntryTime

	// Validate timestamps (must be after year 2000 = ~946684800000 ms)
	minValidTime := int64(946684800000) // 2000-01-01 UTC in milliseconds
	if exitTimeMs < minValidTime {
		return false, nil
	}
	if entryTimeMs < minValidTime {
		entryTimeMs = exitTimeMs
	}
	if entryTimeMs > exitTimeMs {
		entryTimeMs = exitTimeMs
	}

	nowMs := time.Now().UTC().UnixMilli()
	pos := &TraderPosition{
		TraderID:           traderID,
		ExchangeID:         exchangeID,
		ExchangeType:       exchangeType,
		ExchangePositionID: exchangePositionID,
		Symbol:             record.Symbol,
		Side:               side,
		Quantity:           record.Quantity,
		EntryQuantity:      record.Quantity,
		EntryPrice:         record.EntryPrice,
		EntryTime:          entryTimeMs,
		ExitPrice:          record.ExitPrice,
		ExitOrderID:        record.OrderID,
		ExitTime:           exitTimeMs,
		RealizedPnL:        record.RealizedPnL,
		Fee:                record.Fee,
		Leverage:           record.Leverage,
		Status:             "CLOSED",
		CloseReason:        record.CloseType,
		Source:             "sync",
		CreatedAt:          nowMs,
		UpdatedAt:          nowMs,
	}

	err = s.db.Create(pos).Error
	if err != nil {
		if strings.Contains(err.Error(), "UNIQUE constraint failed") {
			return false, nil
		}
		return false, fmt.Errorf("failed to create position from closed PnL: %w", err)
	}

	return true, nil
}

// GetLastClosedPositionTime gets the most recent exit time (Unix ms)
func (s *PositionStore) GetLastClosedPositionTime(traderID string) (int64, error) {
	var pos TraderPosition
	err := s.db.Where("trader_id = ? AND status = ? AND exit_time > 0", traderID, "CLOSED").
		Order("exit_time DESC").
		First(&pos).Error

	if err == gorm.ErrRecordNotFound || pos.ExitTime == 0 {
		return time.Now().UTC().Add(-30 * 24 * time.Hour).UnixMilli(), nil
	}
	if err != nil {
		return 0, fmt.Errorf("failed to get last closed position time: %w", err)
	}

	return pos.ExitTime, nil
}

// SyncClosedPositions syncs closed positions from exchange
func (s *PositionStore) SyncClosedPositions(traderID, exchangeID, exchangeType string, records []ClosedPnLRecord) (int, int, error) {
	created, skipped := 0, 0
	for _, record := range records {
		rec := record
		wasCreated, err := s.CreateFromClosedPnL(traderID, exchangeID, exchangeType, &rec)
		if err != nil {
			return created, skipped, fmt.Errorf("failed to sync position: %w", err)
		}
		if wasCreated {
			created++
		} else {
			skipped++
		}
	}
	return created, skipped, nil
}
