package models

import (
	"time"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

// User repräsentiert einen Benutzer des Systems
type User struct {
	ID           uint          `gorm:"primaryKey" json:"id"`
	Name         string        `json:"name" gorm:"not null"`
	Email        string        `json:"email" gorm:"uniqueIndex;not null"`
	PasswordHash string        `json:"-" gorm:"not null"` // JSON-Tag "-" versteckt das Feld in API-Responses
	BankAccounts []BankAccount `gorm:"foreignKey:UserID" json:"bank_accounts,omitempty"`
	Depots       []Depot       `gorm:"foreignKey:UserID" json:"depots,omitempty"`
	Transactions []Transaction `gorm:"foreignKey:UserID" json:"transactions,omitempty"`
	Categories   []Category    `gorm:"foreignKey:UserID" json:"categories,omitempty"`
	RecurrenceRules []RecurrenceRule `gorm:"foreignKey:UserID" json:"recurrence_rules,omitempty"`
	CreatedAt    time.Time     `json:"created_at"`
	UpdatedAt    time.Time     `json:"updated_at"`
}

// BankAccount repräsentiert ein Bankkonto eines Benutzers
type BankAccount struct {
	ID           uint          `gorm:"primaryKey" json:"id"`
	UserID       uint          `json:"user_id" gorm:"not null"`
	User         User          `gorm:"foreignKey:UserID" json:"user,omitempty"`
	Name         string        `json:"name" gorm:"not null"`
	Bank         string        `json:"bank" gorm:"not null"`
	IBAN         string        `json:"iban"`
	Balance      float64       `json:"balance" gorm:"default:0"`
	AccountType  string        `json:"account_type" gorm:"not null"` // "checking", "savings", "credit"
	Transactions []Transaction `gorm:"foreignKey:BankAccountID" json:"transactions,omitempty"`
	CreatedAt    time.Time     `json:"created_at"`
	UpdatedAt    time.Time     `json:"updated_at"`
}

// Depot repräsentiert ein Wertpapierdepot eines Benutzers
type Depot struct {
	ID          uint      `gorm:"primaryKey" json:"id"`
	UserID      uint      `json:"user_id" gorm:"not null"`
	User        User      `gorm:"foreignKey:UserID" json:"user,omitempty"`
	Name        string    `json:"name" gorm:"not null"`
	Broker      string    `json:"broker" gorm:"not null"`
	DepotNumber string    `json:"depot_number"`
	TotalValue  float64   `json:"total_value" gorm:"default:0"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

// Category repräsentiert eine Transaktionskategorie
type Category struct {
	ID          uint          `gorm:"primaryKey" json:"id"`
	UserID      uint          `json:"user_id" gorm:"not null"`
	User        User          `gorm:"foreignKey:UserID" json:"user,omitempty"`
	Name        string        `json:"name" gorm:"not null"`
	Description string        `json:"description"`
	Color       string        `json:"color" gorm:"default:'#6B7280'"` // Default-Farbe
	Icon        string        `json:"icon" gorm:"default:'💰'"` // Default-Icon
	Transactions []Transaction `gorm:"foreignKey:CategoryID" json:"transactions,omitempty"`
	CreatedAt   time.Time     `json:"created_at"`
	UpdatedAt   time.Time     `json:"updated_at"`
}

// Transaction repräsentiert eine Finanztransaktion
type Transaction struct {
	ID               uint             `gorm:"primaryKey" json:"id"`
	UserID           uint             `json:"user_id" gorm:"not null"`
	User             User             `gorm:"foreignKey:UserID" json:"user,omitempty"`
	BankAccountID    *uint            `json:"bank_account_id"` // Optional: kann NULL sein für Depot-Transaktionen
	BankAccount      *BankAccount     `gorm:"foreignKey:BankAccountID" json:"bank_account,omitempty"`
	CategoryID       *uint            `json:"category_id"` // Optional
	Category         *Category        `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
	Amount           float64          `json:"amount" gorm:"not null"`
	Description      string           `json:"description" gorm:"not null"`
	Type             string           `json:"type" gorm:"not null"` // "income" oder "expense"
	Date             time.Time        `json:"date" gorm:"not null"`
	IsRecurring      bool             `json:"is_recurring" gorm:"default:false"`
	RecurrenceRuleID *uint            `json:"recurrence_rule_id"` // Optional: Link zur Wiederholungsregel
	RecurrenceRule   *RecurrenceRule  `gorm:"foreignKey:RecurrenceRuleID" json:"recurrence_rule,omitempty"`
	CreatedAt        time.Time        `json:"created_at"`
	UpdatedAt        time.Time        `json:"updated_at"`
}

// RecurrenceRule definiert die Regeln für wiederkehrende Transaktionen
type RecurrenceRule struct {
	ID            uint      `gorm:"primaryKey" json:"id"`
	UserID        uint      `json:"user_id" gorm:"not null"`
	User          User      `gorm:"foreignKey:UserID" json:"user,omitempty"`
	
	// Template-Informationen für neue Transaktionen
	Amount        float64   `json:"amount" gorm:"not null"`
	Description   string    `json:"description" gorm:"not null"`
	CategoryID    *uint     `json:"category_id"`
	Category      *Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
	Type          string    `json:"type" gorm:"not null"` // "income" or "expense"
	BankAccountID *uint     `json:"bank_account_id"`
	BankAccount   *BankAccount `gorm:"foreignKey:BankAccountID" json:"bank_account,omitempty"`
	
	// Wiederholungsregeln
	Interval      string    `json:"interval" gorm:"not null"` // "daily", "weekly", "monthly", "yearly"
	IntervalCount int       `json:"interval_count" gorm:"default:1"` // z.B. alle 2 Wochen = weekly + 2
	StartDate     time.Time `json:"start_date" gorm:"not null"`
	EndDate       *time.Time `json:"end_date"` // Optional: NULL bedeutet unbegrenzt
	DayOfWeek     *int      `json:"day_of_week"` // 0=Sonntag, 1=Montag, etc. (für wöchentlich)
	DayOfMonth    *int      `json:"day_of_month"` // 1-31 (für monatlich)
	IsActive      bool      `json:"is_active" gorm:"default:true"`
	LastGenerated *time.Time `json:"last_generated"` // Letztes generiertes Datum
	
	// Beziehungen zu generierten Transaktionen
	Transactions  []Transaction `gorm:"foreignKey:RecurrenceRuleID" json:"transactions,omitempty"`
	
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

// Dashboard-spezifische Strukturen für Auswertungen

// AssetOverview stellt eine Vermögensübersicht dar
type AssetOverview struct {
	TotalBankBalance float64 `json:"total_bank_balance"`
	TotalDepotValue  float64 `json:"total_depot_value"`
	TotalAssets      float64 `json:"total_assets"`
	BankAccounts     []BankAccountSummary `json:"bank_accounts"`
	Depots          []DepotSummary `json:"depots"`
}

// BankAccountSummary für Dashboard-Übersicht
type BankAccountSummary struct {
	ID          uint    `json:"id"`
	Name        string  `json:"name"`
	Bank        string  `json:"bank"`
	AccountType string  `json:"account_type"`
	Balance     float64 `json:"balance"`
}

// DepotSummary für Dashboard-Übersicht
type DepotSummary struct {
	ID         uint    `json:"id"`
	Name       string  `json:"name"`
	Broker     string  `json:"broker"`
	TotalValue float64 `json:"total_value"`
}

// MonthlyStats für monatliche Statistiken
type MonthlyStats struct {
	Month       string  `json:"month"`
	Income      float64 `json:"income"`
	Expenses    float64 `json:"expenses"`
	NetChange   float64 `json:"net_change"`
	Balance     float64 `json:"balance"`
}

// WeeklyStats für wöchentliche Statistiken
type WeeklyStats struct {
	Week        string  `json:"week"`
	Income      float64 `json:"income"`
	Expenses    float64 `json:"expenses"`
	NetChange   float64 `json:"net_change"`
	StartDate   time.Time `json:"start_date"`
	EndDate     time.Time `json:"end_date"`
}

// CategoryStats für Kategorie-Auswertungen
type CategoryStats struct {
	CategoryID   uint    `json:"category_id"`
	CategoryName string  `json:"category_name"`
	TotalAmount  float64 `json:"total_amount"`
	Count        int     `json:"count"`
	Percentage   float64 `json:"percentage"`
}

// TransactionTrend für Verlaufsdaten
type TransactionTrend struct {
	Date    time.Time `json:"date"`
	Income  float64   `json:"income"`
	Expense float64   `json:"expense"`
	Balance float64   `json:"balance"`
}

// DashboardData kombiniert alle Dashboard-Daten
type DashboardData struct {
	UserName         string            `json:"user_name"`
	AssetOverview    AssetOverview     `json:"asset_overview"`
	MonthlyStats     []MonthlyStats    `json:"monthly_stats"`
	WeeklyStats      []WeeklyStats     `json:"weekly_stats"`
	CategoryStats    []CategoryStats   `json:"category_stats"`
	TransactionTrend []TransactionTrend `json:"transaction_trend"`
	RecentTransactions []Transaction   `json:"recent_transactions"`
}

// HashPassword erstellt einen bcrypt Hash des Passworts
func (u *User) HashPassword(password string) error {
	hashedBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	u.PasswordHash = string(hashedBytes)
	return nil
}

// CheckPassword vergleicht das eingegebene Passwort mit dem Hash
func (u *User) CheckPassword(password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password))
	return err == nil
}

// IsRecurringTransaction prüft, ob eine Transaktion wiederkehrend ist
func (t *Transaction) IsRecurringTransaction() bool {
	return t.IsRecurring && t.RecurrenceRule != nil
}

// GetNextOccurrence berechnet das nächste Auftreten einer wiederkehrenden Transaktion
func (r *RecurrenceRule) GetNextOccurrence(from time.Time) *time.Time {
	if !r.IsActive {
		return nil
	}

	var next time.Time
	
	switch r.Interval {
	case "daily":
		next = from.AddDate(0, 0, r.IntervalCount)
	case "weekly":
		next = from.AddDate(0, 0, 7*r.IntervalCount)
		// Wenn ein bestimmter Wochentag gewünscht ist
		if r.DayOfWeek != nil {
			daysUntilTargetDay := (*r.DayOfWeek - int(from.Weekday()) + 7) % 7
			if daysUntilTargetDay == 0 && from.Equal(r.StartDate) {
				daysUntilTargetDay = 7 * r.IntervalCount
			}
			next = from.AddDate(0, 0, daysUntilTargetDay)
		}
	case "monthly":
		next = from.AddDate(0, r.IntervalCount, 0)
		// Wenn ein bestimmter Tag im Monat gewünscht ist
		if r.DayOfMonth != nil && *r.DayOfMonth >= 1 && *r.DayOfMonth <= 31 {
			next = time.Date(next.Year(), next.Month(), *r.DayOfMonth, next.Hour(), next.Minute(), next.Second(), next.Nanosecond(), next.Location())
		}
	case "yearly":
		next = from.AddDate(r.IntervalCount, 0, 0)
	default:
		return nil
	}

	// Prüfen, ob das nächste Datum nach dem Enddatum liegt
	if r.EndDate != nil && next.After(*r.EndDate) {
		return nil
	}

	return &next
}

// CanGenerate prüft, ob eine neue Instanz der wiederkehrenden Transaktion generiert werden kann
func (r *RecurrenceRule) CanGenerate() bool {
	if !r.IsActive {
		return false
	}

	now := time.Now()
	
	// Prüfen, ob wir noch vor dem Enddatum sind
	if r.EndDate != nil && now.After(*r.EndDate) {
		return false
	}

	// Prüfen, ob es Zeit für die nächste Generierung ist
	lastDate := r.StartDate
	if r.LastGenerated != nil {
		lastDate = *r.LastGenerated
	}
	
	nextOccurrence := r.GetNextOccurrence(lastDate)
	if nextOccurrence == nil {
		return false
	}
	
	// Geändert: Generiere Transaktionen für die nächsten 12 Monate (für Dashboard-Trend)
	future12Months := now.AddDate(1, 0, 0)
	return !nextOccurrence.After(future12Months)
}

// GenerateTransaction erstellt eine neue Transaktion basierend auf der Wiederholungsregel
func (r *RecurrenceRule) GenerateTransaction(db *gorm.DB) (*Transaction, error) {
	if !r.CanGenerate() {
		return nil, nil // Keine Transaktion zu generieren
	}

	// Nächstes Datum berechnen
	lastDate := r.StartDate
	if r.LastGenerated != nil {
		lastDate = *r.LastGenerated
	}
	
	nextDate := r.GetNextOccurrence(lastDate)
	if nextDate == nil {
		return nil, nil
	}

	// Neue Transaktion erstellen
	transaction := &Transaction{
		UserID:            r.UserID,
		BankAccountID:     r.BankAccountID,
		Amount:            r.Amount,
		Description:       r.Description,
		CategoryID:        r.CategoryID,
		Type:              r.Type,
		Date:              *nextDate,
		IsRecurring:       true,
		RecurrenceRuleID:  &r.ID,
	}

	// Transaktion in der Datenbank speichern
	if err := db.Create(transaction).Error; err != nil {
		return nil, err
	}

	// LastGenerated aktualisieren
	r.LastGenerated = nextDate
	if err := db.Save(r).Error; err != nil {
		return nil, err
	}

	return transaction, nil
}

// Migrate führt die Datenbank-Migration durch
func Migrate(db *gorm.DB) error {
	return db.AutoMigrate(&User{}, &Category{}, &Transaction{}, &BankAccount{}, &Depot{}, &RecurrenceRule{})
}
