package api

import (
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type Transaction struct {
	gorm.Model
	MemberNum int       `json:"-" gorm:"column:member"`
	Member    Member    `json:"member" gorm:"foreignKey:MemberNum;references:Num"`
	Date      time.Time `json:"date"`
	Total     int       `json:"total"`
	Type      string    `json:"type"`

	Purchase []Purchase `json:"purchase"`
	Topup    Topup      `json:"topup"`
}

func (a *api) ListTransactions(w http.ResponseWriter, req *http.Request) {
	var transactions []Transaction
	err := a.db.Preload("Purchase.Product").
		Preload(clause.Associations).
		Order("date desc").
		Find(&transactions).Error
	if err != nil {
		log.Printf("Can't list transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(transactions)
	if err != nil {
		log.Printf("Can't encode transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func (a *api) GetTransaction(num int, role string, w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	var transaction Transaction
	err := a.db.Preload("Purchase.Product").
		Preload(clause.Associations).
		First(&transaction, vars["id"]).Error
	if err != nil {
		if err.Error() == "record not found" {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		log.Printf("Can't get transaction %s: %v", vars["code"], err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if transaction.MemberNum != num && role != "admin" {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(transaction)
	if err != nil {
		log.Printf("Can't encode transaction: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (a *api) GetMemberTransactions(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	num, _ := strconv.Atoi(vars["num"])
	a.getTransactionsByMember(num, w, req)
}

func (a *api) getTransactionsByMember(num int, w http.ResponseWriter, req *http.Request) {
	var transactions []Transaction
	err := a.db.Where("member = ?", num).
		Preload("Purchase.Product").
		Preload(clause.Associations).
		Find(&transactions).Error
	if err != nil {
		log.Printf("Can't list transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(transactions)
	if err != nil {
		log.Printf("Can't encode transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func (a *api) updateMemberBalance(num int, ammount int) int {
	var member Member
	err := a.db.Where("num = ?", num).Find(&member).Error
	if err != nil {
		log.Printf("Can't find member %d: %v", num, err)
		return http.StatusNotAcceptable
	}
	if member.Balance < -ammount {
		log.Printf("Member %d don't have enough money (%d-%d)", num, member.Balance, ammount)
		return http.StatusBadRequest
	}
	err = a.db.Model(&Member{}).
		Where("num = ?", num).
		Update("balance", gorm.Expr("balance + ?", ammount)).Error
	if err != nil {
		log.Printf("Can't update update member balance %d-%d: %v", num, ammount, err)
		return http.StatusNotAcceptable
	}
	return http.StatusOK
}