package model

import (
	"errors"
	"time"

	"github.com/openaccounting/oa-server/core/model/types"
	"github.com/openaccounting/oa-server/core/repository"
	"github.com/openaccounting/oa-server/core/util"
	"github.com/openaccounting/oa-server/database"
	"gorm.io/gorm"
)

// GormModel is the GORM-based implementation of the Model
type GormModel struct {
	repository *repository.GormRepository
	bcrypt     util.Bcrypt
	config     types.Config
}

// NewGormModel creates a new GORM-based model
func NewGormModel(gormDB *gorm.DB, bcrypt util.Bcrypt, config types.Config) *GormModel {
	repo := repository.NewGormRepository(gormDB)
	return &GormModel{
		repository: repo,
		bcrypt:     bcrypt,
		config:     config,
	}
}

// CreateModel creates a new model using the existing database connection
func CreateGormModel(bcrypt util.Bcrypt, config types.Config) (*GormModel, error) {
	// Use the existing database connection
	if database.DB == nil {
		return nil, errors.New("database connection not initialized")
	}
	
	return NewGormModel(database.DB, bcrypt, config), nil
}

// Implement the Interface by delegating to the business logic layer
// The business logic layer (existing model methods) will call the repository

// UserInterface methods - delegate to existing business logic
func (m *GormModel) CreateUser(user *types.User) error {
	// The existing business logic in user.go will be updated to use the repository
	// For now, delegate directly to repository for basic operations
	return m.repository.InsertUser(user)
}

func (m *GormModel) VerifyUser(code string) error {
	return m.repository.VerifyUser(code)
}

func (m *GormModel) UpdateUser(user *types.User) error {
	return m.repository.UpdateUser(user)
}

func (m *GormModel) ResetPassword(email string) error {
	// This would need the full business logic from the original model
	// For now, simplified implementation
	user, err := m.repository.GetVerifiedUserByEmail(email)
	if err != nil {
		return err
	}
	
	user.PasswordReset, err = util.NewGuid()
	if err != nil {
		return err
	}
	
	return m.repository.UpdateUserResetPassword(user)
}

func (m *GormModel) ConfirmResetPassword(password string, code string) (*types.User, error) {
	user, err := m.repository.GetUserByResetCode(code)
	if err != nil {
		return nil, err
	}
	
	passwordHash, err := m.bcrypt.GenerateFromPassword([]byte(password), m.bcrypt.GetDefaultCost())
	if err != nil {
		return nil, err
	}
	
	user.PasswordHash = string(passwordHash)
	user.Password = ""
	
	err = m.repository.UpdateUser(user)
	if err != nil {
		return nil, err
	}
	
	return user, nil
}

// AccountInterface methods - delegate to repository
func (m *GormModel) CreateAccount(account *types.Account, userId string) error {
	return m.repository.InsertAccount(account)
}

func (m *GormModel) UpdateAccount(account *types.Account, userId string) error {
	return m.repository.UpdateAccount(account)
}

func (m *GormModel) DeleteAccount(id string, userId string, orgId string) error {
	return m.repository.DeleteAccount(id)
}

func (m *GormModel) GetAccounts(orgId string, userId string, tokenId string) ([]*types.Account, error) {
	return m.repository.GetAccountsByOrgId(orgId)
}

func (m *GormModel) GetAccountsWithBalances(orgId string, userId string, tokenId string, date time.Time) ([]*types.Account, error) {
	accounts, err := m.repository.GetAccountsByOrgId(orgId)
	if err != nil {
		return nil, err
	}
	
	// Add balance calculations
	err = m.repository.AddBalances(accounts, date)
	if err != nil {
		return nil, err
	}
	
	return accounts, nil
}

func (m *GormModel) GetAccount(orgId, accId, userId, tokenId string) (*types.Account, error) {
	return m.repository.GetAccount(accId)
}

func (m *GormModel) GetAccountWithBalance(orgId, accId, userId, tokenId string, date time.Time) (*types.Account, error) {
	account, err := m.repository.GetAccount(accId)
	if err != nil {
		return nil, err
	}
	
	// Add balance calculation
	err = m.repository.AddBalance(account, date)
	if err != nil {
		return nil, err
	}
	
	return account, nil
}

// Complete OrgInterface implementation
func (m *GormModel) CreateOrg(org *types.Org, userId string) error {
	// Get default accounts - this needs to be implemented properly
	accounts := []*types.Account{} // Empty for now, should create default chart of accounts
	return m.repository.CreateOrg(org, userId, accounts)
}

func (m *GormModel) GetOrg(orgId, userId string) (*types.Org, error) {
	return m.repository.GetOrg(orgId, userId)
}

func (m *GormModel) GetOrgs(userId string) ([]*types.Org, error) {
	return m.repository.GetOrgs(userId)
}

func (m *GormModel) UpdateOrg(org *types.Org, userId string) error {
	return m.repository.UpdateOrg(org)
}

func (m *GormModel) CreateInvite(invite *types.Invite, userId string) error {
	return m.repository.InsertInvite(invite)
}

func (m *GormModel) AcceptInvite(invite *types.Invite, userId string) error {
	return m.repository.AcceptInvite(invite, userId)
}

func (m *GormModel) GetInvites(orgId, userId string) ([]*types.Invite, error) {
	return m.repository.GetInvites(orgId)
}

func (m *GormModel) DeleteInvite(inviteId, userId string) error {
	return m.repository.DeleteInvite(inviteId)
}

// SessionInterface implementation
func (m *GormModel) CreateSession(session *types.Session) error {
	return m.repository.InsertSession(session)
}

func (m *GormModel) InsertSession(session *types.Session) error {
	return m.repository.InsertSession(session)
}

func (m *GormModel) DeleteSession(sessionId, userId string) error {
	return m.repository.DeleteSession(sessionId, userId)
}

func (m *GormModel) UpdateSessionActivity(sessionId string) error {
	return m.repository.UpdateSessionActivity(sessionId)
}

// ApiKeyInterface implementation
func (m *GormModel) CreateApiKey(apiKey *types.ApiKey) error {
	return m.repository.InsertApiKey(apiKey)
}

func (m *GormModel) InsertApiKey(apiKey *types.ApiKey) error {
	return m.repository.InsertApiKey(apiKey)
}

func (m *GormModel) UpdateApiKey(apiKey *types.ApiKey) error {
	return m.repository.UpdateApiKey(apiKey)
}

func (m *GormModel) DeleteApiKey(keyId, userId string) error {
	return m.repository.DeleteApiKey(keyId, userId)
}

func (m *GormModel) GetApiKeys(userId string) ([]*types.ApiKey, error) {
	return m.repository.GetApiKeys(userId)
}

func (m *GormModel) UpdateApiKeyActivity(keyId string) error {
	return m.repository.UpdateApiKeyActivity(keyId)
}

// TransactionInterface implementation  
func (m *GormModel) CreateTransaction(transaction *types.Transaction) error {
	return m.repository.InsertTransaction(transaction)
}

func (m *GormModel) UpdateTransaction(transactionId string, transaction *types.Transaction) error {
	return m.repository.DeleteAndInsertTransaction(transactionId, transaction)
}

func (m *GormModel) GetTransactionsByAccount(accountId, orgId, userId string, options *types.QueryOptions) ([]*types.Transaction, error) {
	return m.repository.GetTransactionsByAccount(accountId, options)
}

func (m *GormModel) GetTransactionsByOrg(orgId, userId string, options *types.QueryOptions) ([]*types.Transaction, error) {
	return m.repository.GetTransactionsByOrg(orgId, options, []string{})
}

func (m *GormModel) DeleteTransaction(transactionId, orgId, userId string) error {
	return m.repository.DeleteTransaction(transactionId)
}

func (m *GormModel) InsertTransaction(transaction *types.Transaction) error {
	return m.repository.InsertTransaction(transaction)
}

func (m *GormModel) GetTransactionById(id string) (*types.Transaction, error) {
	return m.repository.GetTransactionById(id)
}

func (m *GormModel) DeleteAndInsertTransaction(id string, transaction *types.Transaction) error {
	return m.repository.DeleteAndInsertTransaction(id, transaction)
}

// PriceInterface implementation
func (m *GormModel) CreatePrice(price *types.Price, userId string) error {
	return m.repository.InsertPrice(price)
}

func (m *GormModel) DeletePrice(priceId, userId string) error {
	// Stub implementation - would need proper implementation
	return nil
}

func (m *GormModel) GetPricesNearestInTime(orgId string, date time.Time, currency string) ([]*types.Price, error) {
	// Stub implementation - would need proper implementation based on specific logic
	return m.repository.GetPrices(orgId, date)
}

func (m *GormModel) GetPricesByCurrency(orgId, currency, userId string) ([]*types.Price, error) {
	// Stub implementation - would need proper implementation based on specific logic
	return m.repository.GetPrices(orgId, time.Now())
}

func (m *GormModel) GetPrices(orgId string, date time.Time) ([]*types.Price, error) {
	return m.repository.GetPrices(orgId, date)
}

func (m *GormModel) InsertPrice(price *types.Price) error {
	return m.repository.InsertPrice(price)
}

// SystemHealthInteface implementation
func (m *GormModel) PingDatabase() error {
	return m.repository.Ping()
}

func (m *GormModel) Ping() error {
	return m.repository.Ping()
}

// BudgetInterface implementation
func (m *GormModel) GetBudget(orgId, userId string) (*types.Budget, error) {
	// Stub implementation - would need proper implementation
	return &types.Budget{}, nil
}

func (m *GormModel) CreateBudget(budget *types.Budget, userId string) error {
	return m.repository.InsertBudget(budget)
}

func (m *GormModel) DeleteBudget(budgetId, userId string) error {
	// Stub implementation - would need proper implementation
	return nil
}

func (m *GormModel) InsertBudget(budget *types.Budget) error {
	return m.repository.InsertBudget(budget)
}

func (m *GormModel) GetBudgets(orgId string) ([]*types.Budget, error) {
	return m.repository.GetBudgets(orgId)
}

// Helper methods
func (m *GormModel) GetOrgUserIds(orgId string) ([]string, error) {
	return m.repository.GetOrgUserIds(orgId)
}