package database

import (
	"GoMembership/internal/constants"
	"GoMembership/internal/models"
	"GoMembership/pkg/logger"
	"crypto/rand"
	"encoding/base64"
	"time"

	"github.com/alexedwards/argon2id"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var DB *gorm.DB

func Open(dbPath string, adminMail string) error {

	db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
	if err != nil {
		return err
	}
	if err := db.AutoMigrate(
		&models.User{},
		&models.SubscriptionModel{},
		&models.Membership{},
		&models.Consent{},
		&models.Verification{},
		&models.Licence{},
		&models.Category{},
		&models.BankAccount{}); err != nil {
		logger.Error.Fatalf("Couldn't create database: %v", err)
		return err
	}
	DB = db

	logger.Info.Print("Opened DB")

	var categoriesCount int64
	db.Model(&models.Category{}).Count(&categoriesCount)
	if categoriesCount == 0 {
		categories := createLicenceCategories()
		for _, model := range categories {
			result := db.Create(&model)
			if result.Error != nil {
				return result.Error
			}
		}
	}

	var subscriptionsCount int64
	db.Model(&models.SubscriptionModel{}).Count(&subscriptionsCount)
	subscriptionModels := createSubscriptionModels()
	for _, model := range subscriptionModels {
		var exists int64
		db.
			Model(&models.SubscriptionModel{}).
			Where("name = ?", model.Name).
			Count(&exists)
		logger.Error.Printf("looked for model.name %v and found %v", model.Name, exists)
		if exists == 0 {
			result := db.Create(&model)
			if result.Error != nil {
				return result.Error
			}
		}
	}

	var userCount int64
	db.Model(&models.User{}).Count(&userCount)
	if userCount == 0 {
		var createdModel models.SubscriptionModel
		if err := db.First(&createdModel).Error; err != nil {
			return err
		}

		admin, err := createAdmin(adminMail, createdModel.ID)
		if err != nil {
			return err
		}
		result := db.Session(&gorm.Session{FullSaveAssociations: true}).Create(&admin)
		if result.Error != nil {
			return result.Error
		}
	}

	return nil
}

func createSubscriptionModels() []models.SubscriptionModel {
	return []models.SubscriptionModel{
		{
			Name:       "Keins",
			Details:    "Dieses Modell ist für Vereinsmitglieder, die keinen Wunsch haben, an dem Carhsharing teilzunehmen.",
			HourlyRate: 999,
			MonthlyFee: 0,
		},
	}
}

func createLicenceCategories() []models.Category {
	return []models.Category{
		{Name: "AM"},
		{Name: "A1"},
		{Name: "A2"},
		{Name: "A"},
		{Name: "B"},
		{Name: "C1"},
		{Name: "C"},
		{Name: "D1"},
		{Name: "D"},
		{Name: "BE"},
		{Name: "C1E"},
		{Name: "CE"},
		{Name: "D1E"},
		{Name: "DE"},
		{Name: "T"},
		{Name: "L"},
	}
}

// TODO: Landing page to create an admin

func createAdmin(userMail string, subscriptionModelID uint) (*models.User, error) {
	passwordBytes := make([]byte, 12)
	_, err := rand.Read(passwordBytes)
	if err != nil {
		return nil, err
	}

	// Encode into a URL-safe base64 string
	password := base64.URLEncoding.EncodeToString(passwordBytes)[:12]

	hash, err := argon2id.CreateHash(password, argon2id.DefaultParams)
	if err != nil {
		return nil, err
	}

	logger.Error.Print("==============================================================")
	logger.Error.Printf("Admin Email: %v", userMail)
	logger.Error.Printf("Admin Password: %v", password)
	logger.Error.Print("==============================================================")

	return &models.User{
		FirstName:   "ad",
		LastName:    "min",
		DateOfBirth: time.Now().AddDate(-20, 0, 0),
		Password:    hash,
		Address:     "Downhill 4",
		ZipCode:     "99999",
		City:        "TechTown",
		Phone:       "0123455678",
		Email:       userMail,
		Status:      constants.ActiveStatus,
		RoleID:      constants.Roles.Admin,
		Membership: models.Membership{
			Status:              constants.DisabledStatus,
			StartDate:           time.Now(),
			SubscriptionModelID: subscriptionModelID,
		},
		BankAccount: models.BankAccount{},
		Licence: &models.Licence{
			Status: constants.UnverifiedStatus,
		},
	}, nil
	//"DE49700500000008447644", //fake
}

func Close() error {
	logger.Info.Print("Closing  DB")
	db, err := DB.DB()
	if err != nil {
		return err
	}
	return db.Close()
}
