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 InitialRelationDTO struct {
	Id                      uint                    `json:"id"`
	ReferenceNumber         string                  `json:"reference_number"`
	TransactionType         string                  `json:"transaction_type"`
	InitialBalanceType      string                  `json:"initial_balance_type"`
	InitialBalanceTypeLabel string                  `json:"initial_balance_type_label"`
	Party                   Party                   `json:"party"`
	Bank                    *bankDTO.BankRelationDTO `json:"bank"`
	Direction               string                  `json:"direction"`
	Nominal                 float64                 `json:"nominal"`
	Notes                   string                  `json:"notes"`
}

type InitialListDTO struct {
	InitialRelationDTO
	CreatedBy     uint                            `json:"created_by"`
	CreatedByUser userDTO.UserRelationDTO         `json:"created_by_user,omitempty"`
	CreatedAt     time.Time                       `json:"created_at"`
	UpdatedAt     time.Time                       `json:"updated_at"`
	Approval      approvalDTO.ApprovalRelationDTO `json:"approval"`
}

type InitialDetailDTO struct {
	InitialListDTO
}

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

// === Mapper Functions ===

func ToInitialRelationDTO(e entity.Payment) InitialRelationDTO {
	reference := ""
	if e.ReferenceNumber != nil {
		reference = *e.ReferenceNumber
	}

	initialBalanceType := initialBalanceTypeFromPayment(e)
	return InitialRelationDTO{
		Id:                      e.Id,
		ReferenceNumber:         reference,
		TransactionType:         transactionTypeLabel(e.TransactionType),
		InitialBalanceType:      initialBalanceType,
		InitialBalanceTypeLabel: initialBalanceLabel(initialBalanceType),
		Party:                   partyFromInitial(e),
		Bank:                    bankFromInitial(e),
		Direction:               e.Direction,
		Nominal:                 e.Nominal,
		Notes:                   e.Notes,
	}
}

func ToInitialListDTO(e entity.Payment) InitialListDTO {
	approval := approvalDTO.ApprovalRelationDTO{}
	if e.LatestApproval != nil {
		approval = approvalDTO.ToApprovalDTO(*e.LatestApproval)
	}

	return InitialListDTO{
		InitialRelationDTO: ToInitialRelationDTO(e),
		CreatedBy:          e.CreatedBy,
		CreatedByUser:      userFromInitial(e),
		CreatedAt:          e.CreatedAt,
		UpdatedAt:          e.UpdatedAt,
		Approval:           approval,
	}
}

func ToInitialListDTOs(e []entity.Payment) []InitialListDTO {
	result := make([]InitialListDTO, len(e))
	for i, r := range e {
		result[i] = ToInitialListDTO(r)
	}
	return result
}

func ToInitialDetailDTO(e entity.Payment) InitialDetailDTO {
	return InitialDetailDTO{
		InitialListDTO: ToInitialListDTO(e),
	}
}

func partyFromInitial(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 bankFromInitial(e entity.Payment) *bankDTO.BankRelationDTO {
	if e.BankWarehouse.Id == 0 {
		return nil
	}
	bank := bankDTO.ToBankRelationDTO(e.BankWarehouse)
	return &bank
}

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

func transactionTypeLabel(transactionType string) string {
	if strings.EqualFold(transactionType, string(utils.TransactionTypeSaldoAwal)) {
		return "Saldo Awal"
	}
	return transactionType
}

func initialBalanceLabel(balanceType string) string {
	switch strings.ToUpper(strings.TrimSpace(balanceType)) {
	case "NEGATIVE":
		return "Saldo Awal Negatif"
	case "POSITIVE":
		return "Saldo Awal Positif"
	default:
		return balanceType
	}
}

func initialBalanceTypeFromPayment(e entity.Payment) string {
	if e.Nominal < 0 {
		return "NEGATIVE"
	}
	return "POSITIVE"
}
