package dto

import (
	"strings"
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
	bankDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks/dto"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
)

// === DTO Structs ===

type TransactionRelationDTO struct {
	Id              uint                    `json:"id"`
	PaymentCode     string                  `json:"payment_code"`
	ReferenceNumber *string                 `json:"reference_number,omitempty"`
	TransactionType string                  `json:"transaction_type"`
	Party           Party                   `json:"party"`
	PaymentDate     time.Time               `json:"payment_date"`
	PaymentMethod   string                  `json:"payment_method"`
	Bank            *bankDTO.BankRelationDTO `json:"bank"`
	ExpenseAmount   float64                 `json:"expense_amount"`
	IncomeAmount    float64                 `json:"income_amount"`
	Nominal         float64                 `json:"nominal"`
	Notes           string                  `json:"notes"`
	CreatedUser     userDTO.UserRelationDTO `json:"created_user,omitempty"`
}

type TransactionListDTO struct {
	Id              uint                            `json:"id"`
	PaymentCode     string                          `json:"payment_code"`
	ReferenceNumber *string                         `json:"reference_number"`
	TransactionType string                          `json:"transaction_type"`
	Party           Party                           `json:"party"`
	PaymentDate     time.Time                       `json:"payment_date"`
	PaymentMethod   string                          `json:"payment_method"`
	Bank            *bankDTO.BankRelationDTO        `json:"bank"`
	ExpenseAmount   float64                         `json:"expense_amount"`
	IncomeAmount    float64                         `json:"income_amount"`
	Nominal         float64                         `json:"nominal"`
	Notes           string                          `json:"notes"`
	CreatedUser     userDTO.UserRelationDTO         `json:"created_user"`
	CreatedAt       time.Time                       `json:"created_at"`
	UpdatedAt       time.Time                       `json:"updated_at"`
	Approval        approvalDTO.ApprovalRelationDTO `json:"approval"`
}

type TransactionDetailDTO struct {
	TransactionListDTO
}

type Party struct {
	Id            uint   `json:"id"`
	Name          string `json:"name"`
	Type          string `json:"type"`
	AccountNumber string `json:"account_number"`
}

// === Mapper Functions ===

func ToTransactionRelationDTO(e entity.Payment) TransactionRelationDTO {
	expenseAmount, incomeAmount := paymentAmounts(e.Direction, e.Nominal)

	return TransactionRelationDTO{
		Id:              e.Id,
		PaymentCode:     paymentCodeFromPayment(e),
		ReferenceNumber: e.ReferenceNumber,
		TransactionType: transactionTypeFromPayment(e),
		Party:           partyFromPayment(e),
		PaymentDate:     e.PaymentDate,
		PaymentMethod:   e.PaymentMethod,
		Bank:            bankFromPayment(e),
		ExpenseAmount:   expenseAmount,
		IncomeAmount:    incomeAmount,
		Nominal:         e.Nominal,
		Notes:           e.Notes,
		CreatedUser:     userFromPayment(e),
	}
}

func ToTransactionListDTO(e entity.Payment) TransactionListDTO {
	expenseAmount, incomeAmount := paymentAmounts(e.Direction, e.Nominal)
	approval := approvalDTO.ApprovalRelationDTO{}
	if e.LatestApproval != nil {
		approval = approvalDTO.ToApprovalDTO(*e.LatestApproval)
	}

	return TransactionListDTO{
		Id:              e.Id,
		PaymentCode:     paymentCodeFromPayment(e),
		ReferenceNumber: e.ReferenceNumber,
		TransactionType: transactionTypeFromPayment(e),
		Party:           partyFromPayment(e),
		PaymentDate:     e.PaymentDate,
		PaymentMethod:   e.PaymentMethod,
		Bank:            bankFromPayment(e),
		ExpenseAmount:   expenseAmount,
		IncomeAmount:    incomeAmount,
		Nominal:         e.Nominal,
		Notes:           e.Notes,
		CreatedUser:     userFromPayment(e),
		CreatedAt:       e.CreatedAt,
		UpdatedAt:       e.UpdatedAt,
		Approval:        approval,
	}
}

func ToTransactionListDTOs(e []entity.Payment) []TransactionListDTO {
	result := make([]TransactionListDTO, len(e))
	for i, r := range e {
		result[i] = ToTransactionListDTO(r)
	}
	return result
}

func ToTransactionDetailDTO(e entity.Payment) TransactionDetailDTO {
	return TransactionDetailDTO{
		TransactionListDTO: ToTransactionListDTO(e),
	}
}

func partyFromPayment(e entity.Payment) Party {
	party := Party{
		Id:            e.PartyId,
		Type:          e.PartyType,
	}
	if e.PartyAccountNumber != nil {
		party.AccountNumber = *e.PartyAccountNumber
	}

	switch utils.PaymentParty(e.PartyType) {
	case utils.PaymentPartyCustomer:
		if e.Customer != nil && e.Customer.Id != 0 {
			party.Name = e.Customer.Name
			if party.AccountNumber == "" {
				party.AccountNumber = e.Customer.AccountNumber
			}
		}
	case utils.PaymentPartySupplier:
		if e.Supplier != nil && e.Supplier.Id != 0 {
			party.Name = e.Supplier.Name
			if party.AccountNumber == "" && e.Supplier.AccountNumber != nil {
				party.AccountNumber = *e.Supplier.AccountNumber
			}
		}
	}

	return party
}

func bankFromPayment(e entity.Payment) *bankDTO.BankRelationDTO {
	if e.BankWarehouse.Id == 0 {
		return nil
	}
	bank := bankDTO.ToBankRelationDTO(e.BankWarehouse)
	return &bank
}

func userFromPayment(e entity.Payment) userDTO.UserRelationDTO {
	if e.CreatedUser.Id == 0 {
		return userDTO.UserRelationDTO{}
	}
	return userDTO.ToUserRelationDTO(e.CreatedUser)
}

func paymentCodeFromPayment(e entity.Payment) string {
	if e.PaymentCode != "" {
		return e.PaymentCode
	}
	if e.ReferenceNumber != nil {
		return *e.ReferenceNumber
	}
	return ""
}

func transactionTypeFromPayment(e entity.Payment) string {
	if e.TransactionType != "" {
		return e.TransactionType
	}
	return e.Direction
}

func paymentAmounts(direction string, nominal float64) (float64, float64) {
	switch strings.ToUpper(direction) {
	case "IN":
		return 0, nominal
	case "OUT":
		return nominal, 0
	default:
		return 0, 0
	}
}
